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;
}
posted @ 2021-06-03 14:25  cjTQX  阅读(75)  评论(0编辑  收藏  举报