这是一道为了说明校赛数据范围的题,一共三题这是第三道【前两道就是a+b那种的】被卡了很久。
下周就是校赛了,希望自己这只小菜鸡可以成为预备役~
【题目描述】
给你一颗带权的n个点的无向树T,另外还有一个n个点的无向完全图G,G中u-v的边的流量等于T中两个点的距离请计算完全图中所有点对的最小割的和。其中,(u,v)和(v,u)算做一个点对
//不知道算不算侵犯了学长的版权。。。如果学长看到了。。。我就删掉
【思考】
之前从来没有接触过网络流,所以刚看到最小割是一脸懵逼的,然后百度了最小割最大流,然后搞了很久才搞明白样例
“边的流量等于T中两个点的距离”这句话的意思是,完全图上的边权是树上两个点最短的那条路径的边权之和
“点对的最小割”把一个点从完全图中孤立出来的最小割是最小的,而一个点对(u,v)就是看是孤立u还是孤立v得到的最小割最小
最后把任意两个点对的最小割求和即为所求
呼~把题目解释完了。
所以这题转化为:求任意一个点到所有点的距离之和,两两比较求得的这个和取小的那一个,也就是把和排序(从小到大),第i个被选了(n-i)次,所以这题的关键就是求任意一点到所有点的距离之和,可是n2的算法很显然超时,这题要用O(n)或者nlgn的算法
这时学长又提醒我们可以用dp做
二次换根,树上dp很常用的一个技巧学长说的
第一个dfs先求f[x],表示以x为根的子树,x到它所有子孙的距离之和,先dfs再计算(回溯时计算)
第二个dfs求dp[x],表示x到所有结点的距离之和,先计算再dfs(父节点先被计算)
显然,第一个dfs有用的地方在于它的根节点(可能还有计算出的它的子孙数目或者深度之类的也可以在第二次拿来用)
f[root]=dp[root]
然后就完事儿,和网络流一点关系都没有
这题还卡了ll,所以要用__int128,__int128需要自己手动写输入输出,而且进行计算的时候要强制转换不然会爆
贴上弱渣代码
#include <bits/stdc++.h>
using namespace std;
const int MAX=100000;
int cnt,head[MAX+5],sz[MAX+5],t,n;
__int128 f[MAX+5],dp[MAX+5],ans;
struct node{
int v,w,next;
}edge[MAX*2+5];
void add_edge(int u,int v,int w){
edge[++cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
}
void dfs1(int uu,int fa){
__int128 i,vv,ww;
sz[uu]=1;
for(i=head[uu];i!=-1;i=edge[i].next){
vv=edge[i].v;
ww=edge[i].w;
if(vv==fa) continue;
dfs1(vv,uu);
sz[uu]+=sz[vv];
f[uu]=(f[uu]+f[vv]+(__int128)(ww*sz[vv]));
}
}
void dfs2(int uu,int fa){
int i,vv,ww;
if(uu==1) dp[uu]=f[uu];
for(i=head[uu];i!=-1;i=edge[i].next){
vv=edge[i].v;
ww=edge[i].w;
if(vv==fa) continue;
dp[vv]=(dp[uu]-(__int128)((2*sz[vv]-n)*ww));
dfs2(vv,uu);
}
}
void _print(__int128 x){
if(x>9) _print(x/10);
putchar(x%10+'0');
}
void print(__int128 x){
if(x<0){
x=-x;
putchar('-');
}
_print(x);
}
int main(){
freopen("3.in","r",stdin);
freopen("3.out","w",stdout);
int i,u,v,w;
scanf("%d",&t);
while(t--){
cnt=0;
ans=0;
memset(head,-1,sizeof(head));
memset(sz,0,sizeof(sz));
memset(f,0,sizeof(f));
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs1(1,-1);
dfs2(1,-1);
sort(dp+1,dp+1+n);
for(i=1;i<=n;i++){
ans+=((__int128)dp[i])*((__int128)(n-i));
}
print(ans);
printf("\n");
}
return 0;
}