【校内模拟】层流 【树链剖分】

校内传送门

题目大意

给出一棵树,给出N条边,问这些边两两之间是否满足两个条件之一①覆盖对方或被对方覆盖 ②没有相交。如果都满足输出Yes,否则输出No。

题解

考场上第一眼就觉得是树剖,毕竟前段时间天天见到这种类型的东西。

做法有点差分的意思,在每条边的两个顶点异或上某个值,然后查询这条边上的异或和,如果合法异或和当然为0。

但这样一来有个问题,就是一条被覆盖的边,其顶点可能与覆盖它的那条边的顶点之一重合,统计的时候就会出问题。考场上想了很久,最后逼不得已在树剖外层写了个特判,结果 0 pts滚粗。

说起来方法其实很简单,只需要把边按长度从小到大排个序再依次加入就完成了~

至于为什么调试半天只有98pts我也很纳闷,但方法应该没错

#include
#define rint register int
#define iint inline int
#define ivoid inline void
#define endll '\n'
#define ll long long
using namespace std;
const int N=1e6+5;
const int M=3e3+5;
const int inf=0x3f3f3f3f;
int m,n,q,k,x,y,z,u,v,w,s,t,l,r,ql,qr,flag;
int a[N],b[N],a1[N],b1[N],head[N],dis[N],vis[N];
int fa[N],deep[N],size[N],son[N],id[N],top[N],ori[N],val[N<<2];
int sum,cnt,ans,res,num,tot;
struct Edge{int v,next;}edge[N<<2];//树上的边
struct Acc{int u,v,len;}acc[N<<2];//要统计的边
iint rad()
{
	int x=0,f=1;char c;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}

ivoid addedge(int u,int v){edge[++cnt]=(Edge){v,head[u]},head[u]=cnt;}

ivoid dfs1(int x)
{
	size[x]=1;
	for(rint i=head[x];i;i=edge[i].next){
		int d=edge[i].v;
		if(d==fa[x])continue;
		fa[d]=x,deep[d]=deep[x]+1,dfs1(d);
		if(size[d]>size[son[x]])son[x]=d;
		size[x]+=size[d];
	}
}

ivoid dfs2(int x,int op)
{
	id[x]=++tot;
	ori[tot]=x;
	top[x]=op;
	if(!son[x])return;
	dfs2(son[x],op);
	for(rint i=head[x];i;i=edge[i].next){
		int d=edge[i].v;
		if(d==son[x]||d==fa[x])continue;
		dfs2(d,d);
	}
}

ivoid pushup(int n){val[n]=val[n<<1]^val[(n<<1)|1];}
ivoid build(int n,int l,int r)
{
	if(l==r){return;}
	int mid=(l+r)>>1;
	build(n<<1,l,mid);build((n<<1)|1,mid+1,r);
	pushup(n);
}

ivoid query(int n,int l,int r,int ql,int qr)
{
	if(l>=ql&&r<=qr){ans^=val[n];return;}
	int mid=(l+r)>>1;
	if(mid>=ql)query(n<<1,l,mid,ql,qr);
	if(mid<qr)query((n<<1)|1,mid+1,r,ql,qr);
}

ivoid change_one(int n,int l,int r,int pos,int change)
{
	if(l==r){val[n]^=change;return;}
	int mid=(l+r)>>1;
	if(mid>=pos)change_one(n<<1,l,mid,pos,change);
	else change_one((n<<1)|1,mid+1,r,pos,change);
	pushup(n);
}

ivoid Lca_query(int x,int y)
{
	ans=12321;
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		ql=id[top[x]],qr=id[x],query(1,1,n,ql,qr);
		x=fa[top[x]];
	}
	if(deep[x]<deep[y])swap(x,y);
	ql=id[y],qr=id[x],query(1,1,n,ql,qr);
	if(ans!=12321){cout<<"No";exit(0);}
}

iint Lca(int x,int y)
{
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		x=fa[top[x]];}
	if(deep[x]<deep[y])swap(x,y);
	return y;
}

iint cmp(Acc q1,Acc q2)
{
	return q1.len<q2.len;
}

int main()
{
//	freopen("quence.in","r",stdin);
//	freopen("quence.out","w",stdout);
	n=rad();m=rad();srand(time(0));
	int size=40<<20;
    __asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个 
	for(rint i=1;i<n;i++)u=rad(),v=rad(),addedge(u,v),addedge(v,u);
	dfs1(1);dfs2(1,1);
	for(rint i=1;i<=m;i++){
		a1[i]=rad();b1[i]=rad();
		acc[i]=(Acc){a1[i],b1[i],deep[a1[i]]+deep[b1[i]]-2*deep[Lca(a1[i],b1[i])]};
	}
	sort(acc+1,acc+m+1,cmp);build(1,1,n);
	for(rint i=1;i<=m;i++){
		int k=rand();
		change_one(1,1,n,id[acc[i].u],k);change_one(1,1,n,id[acc[i].v],k);
		Lca_query(acc[i].u,acc[i].v);
	}
	cout<<"Yes";
	exit(0);
}

如果有神仙看出来为什么这份代码到不了满分麻烦告诉我,感激不尽~

你可能感兴趣的:(校内模拟,树链剖分)