HDU6431 NewNippori

NewNippori

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

\[\sum_{i=1}^n\sum_{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\n",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;
}

posted on 2020-01-16 15:34  autoint  阅读(308)  评论(0编辑  收藏  举报

导航