两道拟阵交水题

题外话

前几天nomama讲拟阵交,感觉自己跟不上时代了,学点新东西

您打算学习的话,提个小建议:
如果英文好的话,直接看《A Course in Combinatorial Optimization》
如果英文不好,可以《浅谈拟阵的一些拓展及其应用》跟《A Course in Combinatorial Optimization》配合着看

UVA12370 Game of Connect

Shannon Switching Game

做法

定理\(\text{color player}\)获胜的充要条件为存在两棵不交的生成树

证明可以到google翻论文

观察发现,可以转化为找图拟阵与"图拟阵的对偶拟阵"的拟阵交问题

Code

第一次打拟阵交,放份代码(uva上过了,但不清楚数据强度)

#include<bits/stdc++.h>
typedef int LL;
typedef double dl;
#define opt operator
#define pb push_back
#define pii std::pair<LL,LL>
const LL maxn=3e2+9,mod=998244353,inf=0x3f3f3f3f;
LL Read(){
	LL x(0),f(1); char c=getchar();
	while(c<'0' || c>'9'){
		if(c=='-') f=-1; c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
	}return x*f;
}
void Chkmin(LL &x,LL y){
	if(y<x) x=y;
}
void Chkmax(LL &x,LL y){
	if(y>x) x=y;
}
LL add(LL x,LL y){
	return x+=y,x>=mod?x-mod:x;
}
LL dec(LL x,LL y){
	return x-=y,x<0?x+mod:x;
}
LL mul(LL x,LL y){
	return 1ll*x*y%mod;
}
LL Pow(LL base,LL b){
	LL ret(1); while(b){
		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
	}return ret;
}
namespace Uf{
	LL N;
	LL fa[maxn];
	void Init(){
		for(LL i=1;i<=N;++i) fa[i]=i;
	}
	LL Find(LL x){
		return fa[x]==x?x:fa[x]=Find(fa[x]);
	}
	void Merge(LL x,LL y){
		x=Find(x); y=Find(y);
		if(x==y) return;
		fa[x]=y;
	}
}
struct edge{
	LL x,y;
};
LL n,m,T,tim;
LL X1[maxn],X2[maxn],dfn[maxn],low[maxn],mark[maxn];
edge e[maxn];
std::vector<pii> V[maxn];
void Dfs(LL u,LL f){
	dfn[u]=low[u]=++tim;
	for(LL i=0;i<V[u].size();++i){
		LL v(V[u][i].first),id(V[u][i].second);
		if(v==f) continue;
		if(!dfn[v]){
			Dfs(v,u);
			Chkmin(low[u],low[v]);
			if(low[v]>dfn[u]){
				X2[id]=0;
			}
		}else Chkmin(low[u],dfn[v]);
	}
}
void Init_X1(){
	Uf::Init();
	for(LL i=1;i<=m;++i){
		X1[i]=0;
	}
	for(LL i=1;i<=m;++i) if(mark[i]){
		Uf::Merge(e[i].x,e[i].y);
	}
	for(LL i=1;i<=m;++i) if(!mark[i]){
		if(Uf::Find(e[i].x)!=Uf::Find(e[i].y)){
			X1[i]=1;
		}
	}
}
void Init_X2(){
	Uf::Init();
	for(LL i=1;i<=m;++i){
		X2[i]=0;
	}
	for(LL i=1;i<=m;++i) if(!mark[i]){
		Uf::Merge(e[i].x,e[i].y);
	}
	LL flag(1);
	for(LL i=1;i<=n;++i){
		flag&=(Uf::Find(i)==Uf::Find(1));
	}
	if(!flag) return;
	for(LL i=1;i<=m;++i) if(!mark[i]){
		X2[i]=1;
	}
	for(LL i=1;i<=n;++i){
		std::vector<pii>().swap(V[i]);
	}
	for(LL i=1;i<=m;++i) if(!mark[i]){
		LL x(e[i].x),y(e[i].y);
		V[x].pb(pii(y,i)); V[y].pb(pii(x,i));
	}
	tim=0;
	for(LL i=1;i<=n;++i){
		dfn[i]=low[i]=0;
	}
	Dfs(1,0);
}
LL Solve(){
	static LL pre[maxn],lu[maxn];
	std::queue<LL> que;
	Init_X1();
	Init_X2();
	for(LL i=1;i<=m;++i){
		lu[i]=inf;
	}
	while(que.size()) que.pop();
	for(LL i=1;i<=m;++i) if(!mark[i]){
		if(X1[i]){
			que.push(i);
			lu[i]=0;
		}
	}
	for(LL i=1;i<=m;++i){
		pre[i]=0;
	}
	while(que.size()){
		LL u(que.front()); que.pop();
		if(X2[u]){
			LL x(u);
			while(x){
				mark[x]^=1;
				x=pre[x];
			}
			return 1;
		}
		if(mark[u]){
			Uf::Init();
			for(LL i=1;i<=m;++i) if(mark[i] && (i^u)){
				Uf::Merge(e[i].x,e[i].y);
			}
			for(LL i=1;i<=m;++i) if(!mark[i] && lu[i]==inf){
				if(Uf::Find(e[i].x)!=Uf::Find(e[i].y)){
					pre[i]=u;
					lu[i]=lu[u]+1;
					que.push(i);
				}
			}
		}else{
			Uf::Init();
			for(LL i=1;i<=m;++i) if(!mark[i] && (i^u)){
				Uf::Merge(e[i].x,e[i].y);
			}
			LL comp(0);
			for(LL i=1;i<=n;++i) comp+=(Uf::Find(i)==i);
			for(LL i=1;i<=m;++i) if(mark[i] && lu[i]==inf){
				if(comp==1 || (comp==2 && Uf::Find(e[i].x)!=Uf::Find(e[i].y))){
					pre[i]=u;
					lu[i]=lu[u]+1;
					que.push(i);
				}
			}
		}
	}
	return 0;
}
int main(){
	T=Read();
	for(LL ti=1;ti<=T;++ti){
		printf("Case %d: ",ti);
		n=Read(); m=Read();
		for(LL i=1;i<=m;++i){
			LL x(Read()+1),y(Read()+1);
			e[i]=(edge){x,y};
			mark[i]=0;
		}
		Uf::N=n;
		Uf::Init();
		for(LL i=1;i<=m;++i){
			Uf::Merge(e[i].x,e[i].y);
		}
		LL flag(1);
		for(LL i=1;i<=n;++i){
			flag&=(Uf::Find(i)==Uf::Find(1));
		}
		if(!flag){
			puts("NO");
			continue;
		}
		LL num(0);
		while(Solve()){
			++num;
		}
		if(num==n-1) puts("YES");
		else puts("NO");
	}
	return 0;
}

Rainbow Graph

计蒜客

做法

\(G_1\)为Red边与Green边构成的图
\(G_2\)为Green边与Blue边构成的图

观察发现,可以将问题转化为\(G_1\)的对偶拟阵\(G_2\)的对偶拟阵的拟阵交问题

如果这题延续上题,用tarjan判割边的方式写,代码量\(5k\sim 6k\)

但我们发现,这题的\(\text{spfa}\)才是复杂度的瓶颈,故可以将上面tarjan判割边改成\(O(m^2)\)\(\text{dfs}\)做法,代码量\(1k\sim 2k\)

posted @ 2021-01-26 22:34  Grice  阅读(225)  评论(0编辑  收藏  举报