zoukankan      html  css  js  c++  java
  • HDU6431 NewNippori

    NewNippori

    给定 (n) 个点 (m) 条边的无向图,求

    [sum_{i=1}^nsum_{j=1}^{i-1} min{mathrm{maxflow}(i, j), 3} ]

    (sum n ≤ 4 × 10^5,sum m ≤ 7 × 10^5)

    题解

    最大流显然要转化成最小割来考虑。

    2018 Multi-University Training Contest 10 solutions BY 雅礼中学

    首先题目就是要你求每对点之间的最大流对 (3)(min) 的和。也就是要判断

    • 有多少对点满足存在一种方案,删去一条边后不连通。

    • 有多少对点满足存在一种方案,删去两条边后不连通。

    第一种很好算,找出边双连通分量即可。不妨假设现在整张图是边双连通的。

    先搞出DFS生成树来, 容易发现删去的两条边可能是:

    • 某一条边是树边,另一条边是非树边:要求这条树边仅被这条非树边覆盖

    • 两条边都是树边:要求这两条树边被覆盖情况相同。不妨考虑类似 共价大爷游长沙 的思路,即给每条非树边随机一个 (10^{18}) 以内的权值,一个树边的权值为覆盖它的非树边的权值的异或和。然后用上 map 就很好统计了,时间复杂度 (O(n log m))

    是的,好统计。口胡谁TM都会。

    做出生成树之后怎么办呢?考虑再进行一次“边三连通分量”划分,把相互之间最小割 (geq 3) 的分在同一个连通块中。

    • 树边+非树边就能割开的组合:把生成树上的这条树边断掉。

    • 都是树边的组合:有些复杂。因为无向图的DFS树只有返祖边,所以这样的树边一定是连续的一段。

    考虑 (x leftrightarrow y leftrightarrow z leftrightarrow w) 这一条链,其中 ((x,y),(y,z),(z,w)) 被覆盖的情况相同,并且非树边数量要 (geq 2),不然就是上面那个树边+非树边组合了。

    在这种情况下,(y,z) 子树中的非树边一定逃不出它们的子树内部,那么 (y,z) 子树显然可以各自形成一个边三连通块,直接把 ((x,y),(y,z),(z,w)) 这三条边断开就行了。

    但是 (x,w) 子树内的点却可能形成 (geq 3) 的最小割,这两棵子树可能在同一边三连通分量中。怎么办呢?再在 (x,w) 之间连一条边就行了。

    为了做这题,我又写了对拍和造数据。

    IN int64 rd(){
    	return rand()^rand()<<15^(int64)rand()<<30^(int64)rand()<<45^(int64)rand()<<60;
    }
    
    CO int N=1e5+10,M=3e5+10;
    struct edge {int v,next;}e[2*M];
    int head[N],tot;
    
    IN void link(int u,int v){
    	e[++tot]=(edge){v,head[u]},head[u]=tot;
    	e[++tot]=(edge){u,head[v]},head[v]=tot;
    }
    
    int pos[N],low[N],dfn;
    int stk[N],top;
    int col[N],idx;
    vector<int> ecc[N];
    
    void tarjan(int u,int ine){
    	pos[u]=low[u]=++dfn;
    	stk[++top]=u;
    	for(int i=head[u];i;i=e[i].next)if(i!=(ine^1)){
    		if(!pos[e[i].v]){
    			tarjan(e[i].v,i);
    			low[u]=min(low[u],low[e[i].v]);
    		}
    		else low[u]=min(low[u],pos[e[i].v]);
    	}
    	if(low[u]==pos[u]){
    		ecc[++idx].clear();
    		do{
    			int x=stk[top];
    			col[x]=idx,ecc[idx].push_back(x);
    		}while(stk[top--]!=u);
    	}
    }
    
    namespace T{
    	int n,ref[N];
    	edge e[4*M];
    	int head[N],tot;
    
    	IN void init(){
    		fill(head+1,head+n+1,0),tot=1;
    	}
    	IN void link(int u,int v){
    		e[++tot]=(edge){v,head[u]},head[u]=tot;
    		e[++tot]=(edge){u,head[v]},head[v]=tot;
    	}
    
    	int pos[N],dfn;
    	int64 cnt[N];
    	map<int64,int> tree;
    	set<int64> non;
    	map<int64,pair<int,int> > add;
    	int tag[2*M];
    
    	void build(int u,int ine){
    		pos[u]=++dfn,cnt[u]=0;
    		for(int i=head[u];i;i=e[i].next)if(i!=(ine^1)){
    			if(!pos[e[i].v]){
    				tag[i/2]=1;
    				// cerr<<"tree "<<i/2<<endl;
    				build(e[i].v,i);
    				cnt[u]^=cnt[e[i].v];
    			}
    			else if(pos[e[i].v]<pos[u]){
    				int64 x=rd();
    				// cerr<<"rand "<<i/2<<" "<<x<<endl;
    				cnt[u]^=x,cnt[e[i].v]^=x;
    				non.insert(x);
    			}
    		}
    		// cerr<<"rand "<<ine/2<<" "<<cnt[u]<<endl;
    		if(non.count(cnt[u])){
    			tag[ine/2]=0;
    			// cerr<<"non "<<ine/2<<endl;
    			return; // edit 1
    		}
    		if(tree.count(cnt[u])){
    			tag[ine/2]=0,tag[tree[cnt[u]]/2]=0;
    			// cerr<<"tr "<<ine/2<<" "<<tree[cnt[u]]/2<<endl;
    		}
    		tree[cnt[u]]=ine;
    		if(!add.count(cnt[u])) add[cnt[u]]=make_pair(u,0);
    		else add[cnt[u]].second=e[ine^1].v;
    	}
    
    	int vis[N];
    
    	int64 main(){
    		// cerr<<"edge m="<<tot/2<<endl;
    		// for(int i=1;i<=tot/2;++i){
    		// 	int u=e[2*i].v,v=e[2*i+1].v;
    		// 	cerr<<i<<" "<<u<<" "<<v<<endl;
    		// }
    		non.clear(),tree.clear();
    		add.clear();
    		fill(pos+1,pos+n+1,0),dfn=0;
    		fill(tag+1,tag+tot+1,0);
    		build(1,0);
    		for(map<int64,pair<int,int> >::iterator i=add.begin();i!=add.end();++i)
    			if(i->second.first and i->second.second){
    				link(i->second.first,i->second.second);
    				// cerr<<"link "<<i->second.first<<" "<<i->second.second<<endl;
    				tag[tot/2]=1;
    			}
    		// cerr<<"tag="<<endl;
    		// for(int i=1;i<=tot/2;++i) cerr<<i<<" tag="<<tag[i]<<endl;
    		// cerr<<endl;
    		fill(vis+1,vis+n+1,0);
    		int64 ans=0;
    		for(int i=1;i<=n;++i)if(!vis[i]){
    			int siz=0;
    			deque<int> Q(1,i);
    			while(Q.size()){
    				int u=Q.front();Q.pop_front();
    				vis[u]=1,++siz;
    				for(int i=head[u];i;i=e[i].next)if(tag[i/2])
    					if(!vis[e[i].v]) Q.push_back(e[i].v);
    			}
    			ans+=(int64)siz*(siz-1)/2;
    		}
    		// cerr<<"ans="<<3*ans+2*((int64)n*(n-1)/2-ans)<<endl;
    		return 3*ans+2*((int64)n*(n-1)/2-ans);
    	}
    }
    
    int vis[2*M];
    
    int64 solve(int x){
    	idx=0,tarjan(x,0);
    	int64 ans=0;
    	int n=0;
    	for(int i=1;i<=idx;++i) n+=ecc[i].size();
    	for(int i=1;i<=idx;++i) ans+=(int64)ecc[i].size()*(n-ecc[i].size());
    	ans/=2;
    	for(int i=1;i<=idx;++i){
    		// reverse(ecc[i].begin(),ecc[i].end());
    		// cerr<<"ecc=";
    		// for(int j=0;j<(int)ecc[i].size();++j) cerr<<" "<<ecc[i][j];
    		// cerr<<endl;
    		T::n=0;
    		for(int j=0;j<(int)ecc[i].size();++j) T::ref[ecc[i][j]]=++T::n;
    		T::init();
    		for(int j=0;j<(int)ecc[i].size();++j){
    			int u=ecc[i][j];
    			for(int k=head[u];k;k=e[k].next)if(!vis[k]){
    				int v=e[k].v;
    				if(col[v]!=i) continue;
    				T::link(T::ref[u],T::ref[v]);
    				vis[k]=vis[k^1]=1;
    			}
    		}
    		ans+=T::main();
    	}
    	return ans;
    }
    void real_main(){
    	int n=read<int>(),m=read<int>();
    	fill(head+1,head+n+1,0),tot=1;
    	for(int i=1;i<=m;++i) link(read<int>(),read<int>());
    	fill(pos+1,pos+n+1,0),dfn=0;
    	fill(vis+1,vis+2*m+1,0);
    	int64 ans=0;
    	for(int i=1;i<=n;++i)if(!pos[i]) ans+=solve(i);
    	printf("%lld
    ",ans);
    }
    int main(){
    	// freopen("gen.in","r",stdin),freopen("mine.out","w",stdout);
    	srand(20030506);
    	for(int T=read<int>();T--;) real_main();
    	return 0;
    }
    
  • 相关阅读:
    std::auto_ptr
    make_pair
    _stdcall与_cdecl(了解)
    函数名与函数指针(了解)
    空指针与野指针
    std::bind(二)
    C++ map 映照容器
    sql find duplicate
    数量
    sort sign numeric
  • 原文地址:https://www.cnblogs.com/autoint/p/12201507.html
Copyright © 2011-2022 走看看