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\)的路径并集中的节点数量,那么问题就是我们该怎么样去求这个并集的元素数,先看下面这张图image
我们假定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\)

用一张图来描述我们统计答案的过程就是这样的:
image

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
*/
posted @ 2022-10-08 21:37  Hanggoash  阅读(6)  评论(0编辑  收藏  举报
动态线条
动态线条end