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;
}
静渊以有谋,疏通而知事。