CF1299D - Around the World

CF1299D - Around the World

题目大意

给定一张带权无向图,满足经过1号点不存在长度\(>3\)的简单环

求删除1号点所连边的一个子集,使得剩下的边构成的图满足

不存在一条 非完全重复 回路 异或和为0

非完全重复即所有边恰好被经过偶数次的回路

边权\(<32\)


分析

考虑如何判定0回路

1.任意一个回路由同一连通块内的环叠加产生

2.将所有\(\text{dfs}\)树上的环边提取出来,无法加入线性基时则存在0回路

线性基是重要的判断0回路的方法,因此考虑直接将线性基压进状态进行\(dp\)


dp

删除1所连边后,对于每个连通块考虑计算

设连通块内环边的线性基为\(D\)(加入每条都能成功插入,否则直接跳过该连通块)

包含\(C\)条连接1的边

仍需考虑经过1的环边,题目限制了这样的环边在每个连通块内最多有一条

不妨提取这条边,设其所在三元环权为\(L\)

那么转移分为3种

1.不选这个连通块

2.选择连通块内所有边,但是不选三元环,即\(3\cdot 2^{C-2}-1\) (如果存在\(L\))

暴力合并\(dp\)状态中的线性基和\(D\)即可,依次插入\(D\)中的每条基

3.额外再选择\(L\),\(2^{C-2}\)

状压线性基容易发现线性基最多有15个位置可能出现1,可以暴力二进制存下来

实际上,合法的线性基通过高斯消元之后种类非常少,因此复杂度有保证

const int N=1e5+10,P=1e9+7;

int n,m;
vector <Pii> G[N];

ll qpow(ll x,ll k=P-2) {
	ll res=1;
	for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
	return res;
}

#define Gauss   rep(i,0,4) if(d[i]) rep(j,i+1,4) if(d[j]&(1<<i)) d[j]^=d[i];\
				drep(i,4,0) D=(D<<(i+1))|d[i];

int Ins(int &D,int x){
	int d[5];
	rep(i,0,4) d[i]=D&((1<<(i+1))-1),D>>=i+1;
	int f=0;
	drep(i,4,0) if(x&(1<<i)) {
		if(d[i]) x^=d[i];
		else { f=1,d[i]=x; break; }
	}
	if(!f) return 0;
	Gauss;
	return 1;
}

int Uni(int &D,int E){
	if(!E) return 1;
	int d[5];
	rep(i,0,4) d[i]=D&((1<<(i+1))-1),D>>=i+1;
	rep(i,0,4) {
		int x=E&((1<<(i+1))-1); E>>=i+1;
		if(!x) continue;
		int f=0;
		drep(i,4,0) if(x&(1<<i)) {
			if(d[i]) x^=d[i];
			else { f=1,d[i]=x; break; }
		}
		if(!f) return 0;
	}
	Gauss;
	return 1;
}

struct Table{
	int val[1<<15],a[1<<15],c;
	void Add(int x,int v) {
		if(!val[x]) a[c++]=x;
		val[x]+=v,Mod1(val[x]);
	}
	void clr(){
		rep(i,0,c-1) val[a[i]]=0;
		c=0;
	}
} dp[2];

int vis[N],dfn,dis[N],D,F,E[N],L,C;
void dfs(int u) {
	vis[u]=++dfn;
	if(~E[u]) C++;
	for(Pii i:G[u]) if(i.first!=1) {
		int v=i.first;
		if(~E[u] && ~E[v]) L=E[u]^E[v]^i.second; // 找到了一个经过1的三元环
		if(!vis[v]) dis[v]=dis[u]^i.second,dfs(v);
		else if(vis[v]>vis[u]) {
			F&=Ins(D,dis[v]^dis[u]^i.second);
		}
	}
}

int main(){
	n=rd(),m=rd();
	rep(i,1,m) {
		int u=rd(),v=rd(),w=rd();
		G[u].pb(mp(v,w)),G[v].pb(mp(u,w));
	}
	rep(i,1,n) E[i]=-1;
	for(Pii i:G[1]) E[i.first]=i.second;
	int cur=0;
	dp[0].Add(0,1);
	for(Pii i:G[1]) {
		int v=i.first;
		if(vis[v]) continue;
		F=1,D=C=0,L=-1,dfs(v);
		if(!F) continue;
		dp[!cur].clr();
		if(~L) C-=2;
		C=qpow(2,C);
		rep(i,0,dp[cur].c-1) {
			int x=dp[cur].a[i],y=dp[cur].val[x];
			dp[!cur].Add(x,y);
			if(Uni(x,D)) {
				dp[!cur].Add(x,((~L?3ll:1ll)*C-1)*y%P);
				if(~L && Ins(x,L)) dp[!cur].Add(x,1ll*y*C%P);
			}
		}
		cur^=1;
	}
	int ans=0;
	rep(i,0,dp[cur].c-1) (ans+=dp[cur].val[dp[cur].a[i]])%=P;
	printf("%d\n",ans);
}
上一篇:CF1408 - Clusterization Counting


下一篇:In-place Sorting(逆向思维,数组下标用vector代替)