CF720B Cactusophobia【网络流】
CF720B
Description
给定一棵每条边有颜色的仙人掌,请你删去最少的边使得图中没有环,并在边最少的基础上使剩余边的颜色数尽量多,求剩余的颜色数。
仙人掌的性质:每条边至多被包含在一个简单环中。
\(n\le 10000\)。
Solution
根据仙人掌的性质,我们可以将所有简单环提出来分别进行考虑,每条环中需要删去一条边,而其他边都可以保留。
在删掉所有环的前提下使得剩余颜色数最多,这是一个网络流模型,我们首先强制所有颜色不能被删完,得到还剩余多少个环,那么剩余环的数量就是必须减少的颜色的数量。
具体的,我们对颜色及环建点,从 \(s\) 向每个颜色建边,流量为最多能删多少条这样颜色的边,(若有一条不在环上的边为该颜色,那么可以删 \(de\) 次,否则只能删 \(de-1\) 次,其中 \(de\) 为该颜色在所有环中的出现次数)。
从每个环的所有颜色向其连边,流量为 \(1\);从每个环向 \(t\) 连边,流量为 \(1\)。表明将该环删除需要删除其中至少一条边。然后跑出最大流,总环数 \(-\) 最大流即为必须减少的颜色数。
Code
#include<bits/stdc++.h>
using namespace std;
namespace iobuff{
const int LEN=1000000;
char in[LEN+5],out[LEN+5];
char *pin=in,*pout=out,*ed=in,*eout=out+LEN;
inline char gc(void){return pin==ed&&(ed=(pin=in)+fread(in,1,LEN,stdin),ed==in)?EOF:*pin++;}
template<typename T> inline void read(T &x){
static int f;
static char c;
c=gc(),f=1,x=0;
while(c<'0'||c>'9') f=(c=='-'?-1:1),c=gc();
while(c>='0'&&c<='9') x=10*x+c-'0',c=gc();
x*=f;
}
}
using namespace iobuff;
#define pb push_back
const int N=8e5+10,M=N<<1,T=4e5+10;
const int inf=0x3f3f3f3f;
struct node{
int v,w,nxt;
}e[M];
int n,m,co,sum,cnt=1,first[T],d[T],bel[T];
vector<int> cir[T];
inline void add(int u,int v,int w){e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=first[u];first[u]=cnt;}
bool vis[N],tp[T],pd[T];
int pos[N],pred[N],col[T],de[T];
inline void dfs(int u){
tp[u]=1;vis[u]=1;
for(int i=first[u];i;i=e[i].nxt){
if(bel[i>>1]||i==(pos[u]^1)) continue;
int v=e[i].v;
if(tp[v]){
++sum;cir[sum].pb(e[i].w);bel[i>>1]=sum;
int now=u;
while(now!=v){
cir[sum].pb(e[pos[now]].w),bel[pos[now]>>1]=sum;
now=pred[now];
}
}
else pos[v]=i,pred[v]=u,dfs(v);
}
tp[u]=0;
}
int s,t;
namespace flow{
int tot,dis[N],cur[N];
struct edge{
int v,f,nxt;
}e[M];
inline void add(int u,int v,int f){e[++cnt]=(edge){v,f,first[u]};first[u]=cnt;}
inline void Add(int u,int v,int f){add(u,v,f);add(v,u,0);}
inline int bfs(){
memset(dis+1,-1,sizeof(int)*(tot));
queue<int>q;
dis[s]=0;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=first[u];i;i=e[i].nxt){
int v=e[i].v;
if(e[i].f>0&&dis[v]==-1){
dis[v]=dis[u]+1;
q.push(v);
if(v==t) return 1;
}
}
}
return 0;
}
inline int dfs(int u,int f){
if(u==t||f==0) return f;
int used=0;
for(int& i=cur[u];i;i=e[i].nxt){
if(e[i].f>0&&dis[e[i].v]==dis[u]+1){
int w=dfs(e[i].v,min(f,e[i].f));
if(!w) continue;
used+=w;
f-=w;e[i].f-=w;e[i^1].f+=w;
if(!f) break;
}
}
if(!used) dis[u]=-1;
return used;
}
inline int dinic(){
int f=0;
while(bfs()){
memcpy(cur,first,sizeof(int)*(tot+1));
f+=dfs(s,inf);
}
return f;
}
}
using flow::tot;
using flow::Add;
int main(){
read(n);read(m);
for(int i=1,u,v,w;i<=m;++i){
read(u);read(v);read(w);
add(u,v,w);add(v,u,w);d[i]=w;
}
sort(d+1,d+m+1);
co=unique(d+1,d+m+1)-d-1;
for(int i=2;i<=cnt;i+=2) e[i].w=e[i+1].w=lower_bound(d+1,d+co+1,e[i].w)-d;
for(int i=1;i<=n;++i) if(!vis[i]) dfs(i);
for(int i=2;i<=cnt;i+=2) if(!bel[i>>1]) pd[e[i].w]=true;
vector<int> ve;
for(int i=1;i<=sum;++i){
bool flag=false;
for(int j:cir[i]){
col[j]++;
if(col[j]!=1||pd[j]) flag=true;
}
if(flag){for(int j:cir[i]) pd[j]=true;}
else{
for(int j:cir[i]) de[j]++;
ve.push_back(i);
}
for(int j:cir[i]) col[j]--;
}
for(int i=1;i<=co;++i){
if(!de[i]) continue;
d[i]=++tot;
}
int now=tot+1;
tot+=ve.size();s=++tot;t=++tot;
for(int i=1;i<=co;++i){
if(!de[i]) continue;
if(pd[i]) Add(s,d[i],de[i]);
else if(de[i]>1) Add(s,d[i],de[i]-1);
}
for(int i=0;i<ve.size();++i){
int u=ve[i];
for(int j:cir[u]) Add(d[j],i+now,1);
Add(i+now,t,1);
}
printf("%d\n",co-(ve.size()-flow::dinic()));
// cerr<<(double)clock()/CLOCKS_PER_SEC<<endl;
return 0;
}