CF 97 E - Leaders - 随机 - 树上差分 - 圆方树

本文解析了一道关于图论的竞赛题目,通过使用圆方树算法解决了一个复杂的图论问题,涉及多次询问两点路径是否为奇数的问题。作者分享了从初版算法到最终优化方案的思考过程,包括如何通过随机化策略提高算法准确性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:
给你一张图多次询问两点路径是否为奇数。
题解:
一看就是个圆方树
想了想细节挺多就弃疗了
然后想了一个假做法,即随便弄出一个dfs树然后询问路径如果和非树边产生的奇环相交就是yes。这样场上并没有过大样例但是只有一个误差(共1e5组询问)。后来闲的没事把边表和dfs序列random_shuffle了一遍,然后……咦我怎么80分了?然后改成跑十遍这个随机然后就过了……
顺带在cf上也能过。
upd:后来被发现是可以被卡的,加了个奇怪的东西仍然过了。但是只保留一开始的代码(圆方树在后面):

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define N 100010
#define M 100010
#define LOG 20
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int d[N],up[N][LOG],Log[N],vis[N],bel[N],id[N],ql[N];
int qx[N],qy[N],isno[N];lint val[N];vector<int> g[N],to[N];
lint dfs(int x,int fa,int cnt)
{
	memset(up[x],0,sizeof up[x]);
	d[x]=d[fa]+1,vis[x]=1,bel[x]=cnt,up[x][0]=fa;
	for(int i=1;i<=Log[d[x]];i++) up[x][i]=up[up[x][i-1]][i-1];
	for(int i=0,y;i<(int)to[x].size();i++) if((y=to[x][i])^fa)
	{
		if(!vis[y]) val[x]+=dfs(y,x,cnt),g[x].pb(y);
		else if(vis[y]==1&&(d[x]&1)==(d[y]&1)) val[x]++,val[y]--;
	}
	return vis[x]=2,val[x];
}
int getval(int x,int fa=0) { val[x]+=val[fa];Rep(i,g[x]) getval(g[x][i],x);return 0; }
inline int getLCA(int x,int y)
{
	if(d[x]<d[y]) swap(x,y);
	for(int i=Log[d[x]];i>=0;i--)
		if(d[up[x][i]]>=d[y]) x=up[x][i];
	if(x==y) return x;
	for(int i=Log[d[x]];i>=0;i--)
		if(up[x][i]^up[y][i]) x=up[x][i],y=up[y][i];
	return up[x][0];
}
char ss[1000000];int ssl;
int main()
{
	srand((unsigned int)(time(0)-20010817-20010916));
	int n=inn(),m=inn(),x,y;rep(i,2,n) Log[i]=Log[i>>1]+1;
	rep(i,1,m) x=inn(),y=inn(),to[x].pb(y),to[y].pb(x);
	int q=inn(),qcnt=q;rep(i,1,n) id[i]=i;
	rep(i,1,q) qx[i]=inn(),qy[i]=inn(),ql[i]=i;
	for(int Tms=25;qcnt&&Tms;Tms--)
	{
		rep(x,1,n) g[x].clear(),val[x]=0;int cnt=0;
		rep(x,1,n) random_shuffle(to[x].begin(),to[x].end());
		memset(vis,0,sizeof(int)*(n+1)),random_shuffle(id+1,id+n+1);
		rep(i,1,n) if(!vis[id[i]]) d[id[i]]=0,dfs(id[i],0,++cnt),getval(id[i]);
		rep(i,1,qcnt)
		{
			int x=qx[ql[i]],y=qy[ql[i]];
			if(bel[x]^bel[y]) { isno[ql[i]]=1;continue; }
			if((d[x]&1)!=(d[y]&1)) { isno[ql[i]]=0;continue; }
			int c=getLCA(x,y);
			if(val[x]+val[y]-2*val[c]) isno[ql[i]]=0;
			else isno[ql[i]]=1;
		}
		int qc=0;
		rep(i,1,qcnt) if(isno[ql[i]]) ql[++qc]=ql[i];
		qcnt=qc;
	}
	rep(i,1,q)
		if(isno[i]) ss[++ssl]='N',ss[++ssl]='o',ss[++ssl]='\n';
		else ss[++ssl]='Y',ss[++ssl]='e',ss[++ssl]='s',ss[++ssl]='\n';
	return fwrite(ss+1,sizeof(char),ssl,stdout),0;
}

圆方树

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define N 400010
#define LOG 21
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct edges{
	int to,pre,id;
}e[N<<1];int h[N],etop,val[N],vis[N],low[N],dfn[N],dfc,d[N],dys[N],ecnt;
stack<int> s,es;int bel[N],eb[N],nc,f[N],up[N][LOG],Log[N];pii edg[N];
inline int add_edge(int u,int v,int id) { return e[++etop].to=v,e[etop].pre=h[u],e[etop].id=id,h[u]=etop; }
int tarjan(int x,int fa=0)
{
	s.push(x),vis[x]=1,low[x]=dfn[x]=++dfc;
	for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)^fa)
	{
		if(!vis[y])
		{
			es.push(e[i].id),tarjan(y,x),low[x]=min(low[x],low[y]);
			if(low[y]>=dfn[x])
			{
				edg[++ecnt]=mp(x,++nc);
				for(int z;dfn[z=s.top()]>=dfn[y];s.pop())
					vis[z]=2,edg[++ecnt]=mp(z,nc);
				while(es.top()^e[i].id) eb[es.top()]=nc,es.pop();
				eb[e[i].id]=nc,es.pop();
			}
		}
		else if(vis[y]==1)
		{
			low[x]=min(low[x],dfn[y]);
			if(dfn[y]<dfn[x]) es.push(e[i].id);
		}
	}
	return 0;
}
int dfs(int x,int fa,int cnt)
{
	vis[x]=1,bel[x]=cnt,dys[x]=dys[fa]+1;
	for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)^fa)
	{
		if(!vis[y]) dfs(y,x,cnt);
		else if(vis[y]==1&&(dys[x]&1)==(dys[y]&1)) f[eb[e[i].id]]++;
	}
	return vis[x]=2;
}
int solve(int x,int fa=0)
{
	vis[x]=1,up[x][0]=fa,d[x]=d[fa]+1,f[x]+=f[fa];
	rep(i,1,Log[d[x]]) up[x][i]=up[up[x][i-1]][i-1];
	for(int i=h[x],y;i;i=e[i].pre)
		if(!vis[y=e[i].to]) solve(y,x);
	return 0;
}
inline int getLCA(int x,int y)
{
	if(d[x]<d[y]) swap(x,y);
	for(int i=Log[d[x]];i>=0;i--)
		if(up[x][i][d]>=y[d]) x=up[x][i];
	if(x==y) return x;
	for(int i=Log[d[x]];i>=0;i--)
		if(up[x][i]^up[y][i]) x=up[x][i],y=up[y][i];
	return up[x][0];
}
int main()
{
	int n=inn(),m=inn(),x,y,cnt=0;nc=n;
	rep(i,1,m) x=inn(),y=inn(),add_edge(x,y,i),add_edge(y,x,i);
	rep(i,1,n) if(!vis[i]) tarjan(i);
	
	memset(vis,0,sizeof(int)*(n+1));
	rep(i,1,n) if(!vis[i]) dfs(i,0,++cnt);
	
	memset(h,0,sizeof(int)*(n+1)),etop=0;
	rep(i,1,ecnt)
		add_edge(edg[i].fir,edg[i].sec,0),
		add_edge(edg[i].sec,edg[i].fir,0);
	
	n=nc;
	memset(vis,0,sizeof(int)*(n+1));
	rep(i,2,n) Log[i]=Log[i>>1]+1;
	rep(i,1,n) if(!vis[i]) solve(i);
	
	for(int curq=1,q=inn();curq<=q;curq++)
	{
		int x=inn(),y=inn();
		if(bel[x]^bel[y]) { printf("No\n");continue; }
		if((dys[x]&1)^(dys[y]&1)) { printf("Yes\n");continue; }
		int c=getLCA(x,y);
		if(f[x]+f[y]-f[c]-f[up[c][0]]) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值