Test 2022.10.08
今天是鸡杂专场
T1 跳楼
题意
就是给出每栋楼的高度,然后有两个约束条件,分别对应前一项、前两项的递推关系,然后从第一栋楼开始,求最后能否到达第n栋楼
分析
简简单单一个和前两项有关的递推,注意如果当一栋楼的前两栋楼都不能到达,那么后面的所有楼一定都是不能到达的,所以这个时候就直接可以break掉输出了,有一个小细节就是memset重置vis数组的时候可以改为在输入的时候用循环展开的原理去做,这样不会T掉后面三个点
Code
点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
template <typename T>inline void re(T &x)
{
x=0;
int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-f;
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
x*=f;
return;
}
template <typename T>void wr(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) wr(x/10);
putchar(x%10^'0');
return;
}
const int maxn=1e5+100;
int t,n,d1,d2;int h[maxn];
int abs1(int x){return x>0?x:-x;}
bool check1(int h1,int h2){return abs1(h1-h2)<=d1;}
bool check2(int h1,int h2,int h3){return h1>h2&&h2<h3&&abs1(h1-h3)<=d2;}
int vis[maxn];
int main()
{
re(t);
while(t--)
{
re(n),re(d1),re(d2);
for(register int i=1;i<=n;++i)re(h[i]),vis[i]=0;
if(n==1){puts("Yes");continue;}
vis[1]=1;if(check1(h[1],h[2]))vis[2]=1;
for(register int i=3;i<=n;++i)
{
if(vis[i-1]==0&&vis[i-2]==0)break;
if(vis[i-1]&&check1(h[i-1],h[i])){vis[i]=1;continue;}
if(vis[i-2]&&check2(h[i-2],h[i-1],h[i]))vis[i]=1;
}
if(vis[n])puts("Yes");
else puts("No");
}
return 0;
}
/*
5
1 5 19
10
14 18 5
13 3 8 16 12 4 17 18 20 13 5 14 13 8
8 3 1
12 11 13 7 9 9 16 17
3 17 5
20 20 6
4 1 12
11 9 13 9
*/
T2 Control
题意
给出一个有向图,定义:如果一个点能掌控一个点,那么他的所有儿子都能掌控这个点(有点像递归的定义),换句话说,这个点所引出的所有路径都必须能到达要被掌控的点
分析
从上面的题意转化,我们很简单的就能想到一个暴力思路,\(O(n^2)\)枚举所有点,判断是否一个点是否能被另一个点掌控就行了,实现这个过程只需要建和样例解释相反的边,然后一个很短小的\(dfs\)就行了,下面就只贴dfs的代码了
点击查看代码
bool dfs(int x,int t)//判断x是否能掌控t
{
if(x==t)return 1;
for(register int i=head[x];i;i=E[i].nex)if(!dfs(E[i].v,t))return 0;
if(!head[x])return 0;//这个点没有儿子了
else return 1;
}
正解
我们思考能否简化这张图,建立一个树形的关系结构可以直接形容控制与否的关系(一个点的直接父亲就是他能够控制的点),来通过树上差分或者是\(lca\)等时间代价更小的算法呢?
建图
首先对于我们要加入的点,给出他的儿子节点,在之前构建的图的基础上面,满足题意的点只有给出的所有儿子的\(lca\)以及\(lca\)的所有父亲,那我们就直接在当前点和\(lca\)之间连边,表示当前点可以掌控\(lca\)及其以上的节点,一边加入点,一边更新倍增的F数组和点的深度,注意还要引入一个超级父亲\(x\)
统计答案
所以有了这个图,我们很清楚的知道对于任意两个询问中给出的点,答案就是他们到根节点\(0\)的路径并集中的节点数量,那么问题就是我们该怎么样去求这个并集的元素数,先看下面这张图
我们假定A节点是0号节点,我们现在要求C到H的答案,首先C的深度是3,H的深度是4,用C+H会重复统计的答案就是:他们的\(lca\)的深度和多统计的一个根节点\(0\),所以最后的\(ans\)就是\(3+4-2-1=4\)
现在拓展到三个点的情况:\(C,H,I,E\),先上代码
点击查看代码
scanf("%d",&k);
for(register int i=1;i<=k;i++)scanf("%d",&s[++top]);
sort(s+1,s+top+1,cmp);
int ans=0;
while(top)ans+=dep[s[top]]-dep[LCA(s[top],s[top-1])],top--;
显而易见的是,最后一行代码就是最核心的地方,当然,从左到右按照\(dfn\)序来统计答案也比较重要
按照从左到右的顺序:
首先考虑\(C,H\):我们只统计\(lca\)以下的答案,也就是\(dep[C]-dep[LCA(C,H)=B]=1\)那么还有\(B\)以上没有统计到的部分呢?不着急,往下看
再考虑\(H,I\):我们依然和上面一样只考虑\(lca\)以下的答案,那么就是\(dep[H]-dep[LCA(H,I)=D]=1\)
同理考虑\(I,E\):这时候统计到的答案就是\(dep[I]-dep[LCA(I,E)=B]=2\)
最后考虑\(E,0(A)\):按照我们刚刚的算法,最后统计到的就是\(dep[E]-dep[LCA(E,A)=A]=2\)
用一张图来描述我们统计答案的过程就是这样的:
Code
点击查看代码
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define MAXN 300000
using namespace std;
struct Edge{int v,nex;}E[MAXN*4];
int head[MAXN],tote=0;
void add(int u,int v){E[++tote].v=v;E[tote].nex=head[u];head[u]=tote;}
int fa[MAXN],dep[MAXN];
int F[MAXN][32];
int n,m,k,u,v,q;
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=31;i>=0;i--)
{
if(dep[F[x][i]]<dep[y])continue;
x=F[x][i];
}
if(x==y)return x;
for(int i=31;i>=0;i--)
{
if(F[x][i]==F[y][i])continue;
x=F[x][i];
y=F[y][i];
}
x=F[x][0];
return x;
}
int dfn[MAXN],cnt;
void dfs(int x)
{
dfn[x]=++cnt;
for(register int i=head[x];i;i=E[i].nex)
dfs(E[i].v);
return ;
}
void upd(int x){for(int j=1;j<32;j++)F[x][j]=F[F[x][j-1]][j-1];}
void display(int x){printf("%d ",x);if(x==0)return;x=F[x][0];display(x);}
int s[MAXN],top;
bool cmp(int x,int y){return dfn[x]>dfn[y];}
int main()
{
scanf("%d",&n);
dep[0]=1;
for(register int i=1;i<=n;++i)
{
scanf("%d",&k);int lca=0;
for(register int cnt=1;cnt<=k;++cnt)
{
scanf("%d",&v);
if(cnt==1)lca=v;
else lca=LCA(lca,v);
}
// printf("LCA:%d----",lca);
add(lca,i);dep[i]=dep[lca]+1;
F[i][0]=lca,upd(i);
// display(i);
}
dfs(0);
scanf("%d",&q);
while(q--)
{
scanf("%d",&k);
for(register int i=1;i<=k;i++)scanf("%d",&s[++top]);
sort(s+1,s+top+1,cmp);
int ans=0;
while(top)ans+=dep[s[top]]-dep[LCA(s[top],s[top-1])],top--;
printf("%d\n",ans);
}
return 0;
}
/*
8 7
0 1
0 6
0 7
1 2
1 3
1 5
2 4
7
0
1 1
1 1
1 2
2 2 3
0
2 2 6
4
2 2 3
2 3 5
2 4 5
3 2 1 7
*/
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/16770329.html