【洛谷6914】[ICPC2015 WF] Tours(非树边随机边权,树边边权异或)

点此看题面

  • 给定一张(n)个点(m)条边的图,要求用(k)种颜色给所有边染色,使得每个简单环中每种颜色的边出现次数相同。
  • 求所有可以达成条件的(k)
  • (n,mle2000)

想出了一个(O(mlogn))的做法,看到这么小的数据范围懵了半天,最后鼓起勇气写了一发结果过了。

始终在一起的边集

显然我们只需求出一个最大的(k),那么答案就应该是它的全部因数。

粗略一想答案应该是所有环长的(gcd),但我们发现环与环之间可能存在交集,而真正的答案应该是所有环长以及所有交集大小的总(gcd)

这等价于把每个环都拆解成若干部分,因此我们要求的实际上就是所有始终在一起的边集大小的(gcd)

经典非树边随机边权

随便找出图中的一棵生成树,给每条非树边随机一个权值,令每条树边的权值为覆盖它的所有非树边边权的异或和。

这样一来,始终在一起的边集就是边权相同的边集。

直接开个(map)存下每种边权的边数,然后求个(gcd)即可。

代码:(O(mlogn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 2000
#define ull unsigned long long
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,ee,lnk[N+5];struct edge {int to,nxt;}e[2*N+5];
I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
int d,dfn[N+5],ct;ull a[N+5],s[N+5];map<ull,int> p;I void dfs(CI x,CI lst)//dfs扫一遍
{
	#define R() ((1ull*rand()*rand()*rand()*rand())^(1ull*rand()*rand()*rand()*rand()))//随机一个边权
	dfn[x]=++d;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(dfn[e[i].to]
		?dfn[e[i].to]<dfn[x]&&(++p[s[++ct]=R()],a[e[i].to]^=s[ct],a[x]^=s[ct]):(dfs(e[i].to,x),a[x]^=a[e[i].to]));//非树边随机边权,树边继续dfs
	a[x]&&++p[a[x]];//统计每种边权的出现次数(a[x]=0表示不在环上,忽略)
}
int main()
{
	RI i,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
	RI g=0;for(i=1;i<=n;++i) dfn[i]?a[i]&&(g=gcd(g,p[a[i]])):(dfs(i,0),0);//统计树边所在边集gcd
	for(i=1;i<=ct;++i) g=gcd(g,p[s[i]]);for(i=1;i<=g;++i) !(g%i)&&printf("%d ",i);return 0;//统计非树边所在边集gcd;所有因数都是答案
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6914.html