题外话
前几天nomama讲拟阵交,感觉自己跟不上时代了,学点新东西
您打算学习的话,提个小建议:
如果英文好的话,直接看《A Course in Combinatorial Optimization》
如果英文不好,可以《浅谈拟阵的一些拓展及其应用》跟《A Course in Combinatorial Optimization》配合着看
UVA12370 Game of Connect
Shannon Switching Game
做法
定理:( ext{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判割边的方式写,代码量(5ksim 6k)
但我们发现,这题的( ext{spfa})才是复杂度的瓶颈,故可以将上面tarjan判割边改成(O(m^2))的( ext{dfs})做法,代码量(1ksim 2k)