BZOJ 2597: [Wc2007]剪刀石頭布(費用流)
阿新 • • 發佈:2018-12-16
解題思路
考慮全集-不能構成三元環的個數。如果三個點不能構成三元環,一定有一個點的入度為\(2\),繼續擴充套件,如果一個點的度數為\(3\),則會失去3個三元環。對於一個點來說,它所產生的不能構成三元環的貢獻為\(C (deg[x],2)\),而度數每增加\(1\),對於答案的影響就是\(C(deg[x]+1,2)-C(deg[x],2)=deg[x]\),然後就可以建圖了。考慮把邊當做點,對於一條未確定的邊來說,它只能對兩個節點中的一個產生\(1\)個度數的貢獻,所以讓每個邊向點連流量為1,費用為0的邊。然後讓源點向每條未確定的邊連流量為1,費用為0的邊。再讓每個點向匯點連流量為\(1\)
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<queue> using namespace std; const int MAXN = 100005; const int MAXM = 500005; const int inf = 0x3f3f3f3f; inline int rd(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return f?x:-x; } int n,head[MAXN],cnt=1,to[MAXM<<1],nxt[MAXM<<1],val[MAXM<<1],cost[MAXM<<1]; int deg[MAXN],num,S,T,op[105][105],dis[MAXN],incf[MAXN],pre[MAXN],ans,tmp[105][105]; bool vis[MAXN]; queue<int> Q; inline void add(int bg,int ed,int w,int z){ to[++cnt]=ed,nxt[cnt]=head[bg],val[cnt]=w,cost[cnt]=z,head[bg]=cnt; } bool spfa(){ while(Q.size()) Q.pop(); memset(dis,0x3f,sizeof(dis)); memset(vis,false,sizeof(vis)); Q.push(S);vis[S]=1;incf[S]=inf;dis[S]=0; while(Q.size()){ int x=Q.front();Q.pop();vis[x]=0; for(int i=head[x];i;i=nxt[i]){ int u=to[i]; if(dis[x]+cost[i]<dis[u] && val[i]){ dis[u]=dis[x]+cost[i]; incf[u]=min(incf[x],val[i]); pre[u]=i; if(!vis[u]) vis[u]=1,Q.push(u); } } } return (dis[T]==inf)?0:1; } inline void update(){ int x=T,i; while(x!=S){ i=pre[x]; val[i]-=incf[T]; val[i^1]+=incf[T]; x=to[i^1]; } ans-=incf[T]*dis[T]; } int main(){ n=rd();int x;T=n+2;S=n+1;num=T; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ x=rd();op[i][j]=x; if(x==1) deg[i]++; } for(int i=1;i<=n;i++) if(deg[i]>1) ans-=deg[i]*(deg[i]-1)/2; for(int i=1;i<=n;i++) for(int j=deg[i];j<=n;j++) add(i,T,1,j),add(T,i,0,-j); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(op[i][j]==2){ num++;add(S,num,1,0);add(num,S,0,0); add(num,i,1,0),add(i,num,0,0); add(num,j,1,0),add(j,num,0,0); tmp[i][j]=tmp[j][i]=num; } while(spfa()) update(); ans+=n*(n-1)*(n-2)/6; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(op[i][j]==2){ for(int k=head[tmp[i][j]];k;k=nxt[k]){ if(to[k]==S) continue; if(!val[k]) { if(to[k]==i) op[i][j]=1,op[j][i]=0; else op[j][i]=1,op[i][j]=0; } } } printf("%d\n",ans); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) printf("%d ",op[i][j]); putchar('\n'); } return 0; }