HDU6350 Always Online

Always Online

给定 \(n\) 个点 \(m\) 条边的点仙人掌,求

\[\sum_{1 ≤ s < t ≤ n} (s ⊕ t ⊕ \mathrm{maxflow}(s, t)) \]

其中 \(⊕\) 表示异或。

\(T ≤ 100,n ≤ 10^5,n − 1 ≤ m ≤ 1.5(n − 1),\sum n ≤ 10^6\)

题解

考虑点双缩点。

我们可以把每个环上最小的那条边断掉,把它的容量加到环的其他边上。显然如果路径要经过这个环,那么最小割会优先考虑这条边。

然后我们按照容量从大到小枚举每一条边,使用并查集维护连通块。每次加边的时候,这条边一定是它连接的两个连通块之间的最小割。那么直接记录每一个bit上面的01数量就行了。

还要用unsigned long long……不过ACM赛制还好。

因为有加法操作,所以要做到230。这个WrongAnswer看得我想把HDU的电脑砸了。

https://blog.csdn.net/V5ZSQ/article/details/82453394

CO int N=1e5+10;
struct edge {int u,v,w,f,next;}e[3*N]; // flag
int head[N],tot;
int vis[N],stk[N],top;
vector<int> nw;

IN bool cmp(int i,int j){
	return e[i].w>e[j].w;
}
IN void link(int u,int v,int w){
	e[++tot]=(edge){u,v,w,0,head[u]},head[u]=tot;
}
void dfs(int u,int ine){
	vis[u]=1;
	for(int i=head[u];i;i=e[i].next)if(!e[i].f){
		e[i].f=e[i^1].f=1;
		stk[++top]=i;
		if(!vis[e[i].v]){
			dfs(e[i].v,i);
			continue;
		}
		int mn=e[i].w;
		for(int j=top;j>=1;--j){
			mn=min(mn,e[stk[j]].w);
			e[stk[j]].f=2; // circle edge
			if(e[stk[j]].u==e[i].v) break;
		}
		bool flag=0; // delete only once
		while(1){
			int t=stk[top--];
			if(!flag and e[t].w==mn) flag=1;
			else{
				e[t].w+=mn;
				nw.push_back(t);
			}
			if(e[t].u==e[i].v) break;
		}
	}
	if(ine and e[ine].f!=2) nw.push_back(stk[top--]);
}

int fa[N],siz[N],num[N][31];

int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void real_main(){
	int n=read<int>();
	fill(head+1,head+n+1,0),tot=1;
	for(int m=read<int>();m--;){
		int u=read<int>(),v=read<int>(),w=read<int>();
		link(u,v,w),link(v,u,w);
		assert(w<1e9);
	}
	fill(vis+1,vis+n+1,0);
	top=0,nw.clear();
	dfs(1,0);
	sort(nw.begin(),nw.end(),cmp);
	for(int i=1;i<=n;++i){
		fa[i]=i,siz[i]=1;
		for(int j=30;j>=0;--j) num[i][j]=i>>j&1;
	}
	uint64 ans=0;
	for(int i=0;i<(int)nw.size();++i){
		int u=e[nw[i]].u,v=e[nw[i]].v,w=e[nw[i]].w;
		// cerr<<"e= "<<u<<" "<<v<<" "<<w<<endl;
		u=find(u),v=find(v);
		for(int j=30;j>=0;--j){ // edit 1: 30 for operator+
			uint64 sum=0;
			if(w>>j&1){
				sum+=(uint64)num[u][j]*num[v][j];
				sum+=(uint64)(siz[u]-num[u][j])*(siz[v]-num[v][j]);
			}
			else{
				sum+=(uint64)num[u][j]*(siz[v]-num[v][j]);
				sum+=(uint64)(siz[u]-num[u][j])*num[v][j];
			}
			ans+=sum<<j;
		}
		for(int j=30;j>=0;--j) num[u][j]+=num[v][j];
		fa[v]=u,siz[u]+=siz[v];
	}
	printf("%llu\n",ans);
}
int main(){
	for(int T=read<int>();T--;) real_main();
	return 0;
}

posted on 2020-01-16 20:14  autoint  阅读(168)  评论(0编辑  收藏  举报

导航