Description
给出一棵 n n n个节点的树,边有边权,对这 n n n个节点建一张完全图,图中两点间距离为两点的树上距离,问任意两点在图上的最大流之和
Input
第一行一整数 T T T表示用例组数,每组用例首先输入一整数 n n n表示点数,之后 n − 1 n-1 n−1行每行输入三个整数 u , v , w u,v,w u,v,w表示 u , v u,v u,v之间有一条权值为 w w w的树边
( 1 ≤ T ≤ 10 , 2 ≤ n ≤ 1 0 5 , 1 ≤ w i ≤ 1000 ) (1\le T\le 10,2\le n\le 10^5,1\le w_i\le 1000) (1≤T≤10,2≤n≤105,1≤wi≤1000)
Output
输出任意两点在图上的最大流之和
Sample Input
2
3
1 2 1
2 3 1
5
1 2 1
2 3 1
2 4 1
4 5 2
Sample Output
Case #1: 7
Case #2: 72
Solution
求 s , t s,t s,t两点之间的最大流即为求两点的最小割,进而是将 n n n个点分成两个点集 S , T S,T S,T,使得 s ∈ S , t ∈ T s\in S,t\in T s∈S,t∈T,且 ∑ u ∈ S , v ∈ T d i s ( u , v ) \sum\limits_{u\in S,v\in T}dis(u,v) u∈S,v∈T∑dis(u,v)最小,以黑白染色来确定每点所属集合,为使两个集合之间的距离和最小,集合内的距离和应尽可能大,故除 s , t s,t s,t外其余点应同色,故 s , t s,t s,t之间最大流即为 m i n ( ∑ d i s ( u , i ) , ∑ d i s ( v , i ) ) min(\sum dis(u,i),\sum dis(v,i)) min(∑dis(u,i),∑dis(v,i)),只要求出一点到其余所有点的距离和即可
第一遍树形 D P DP DP求出所有点到根节点的距离和,第二遍树形 D P DP DP以 d p [ u ] dp[u] dp[u]表示所有点到 u u u点距离和,假设根节点为 1 1 1,那么 d p [ 1 ] dp[1] dp[1]在第一遍时已经求出,进而对于当前点 v v v,其父亲 u u u的 d p dp dp值已经确定,所有点中位于 v v v子树的点到达 u u u比到达 v v v多走了 w w w( w w w为 u , v u,v u,v之间边权),其余点到达 u u u比到达 v v v少走了 w w w,故有转移
d p [ v ] = d p [ u ] + ( n − 2 ⋅ s i z e ( v ) ) ⋅ w dp[v]=dp[u]+(n-2\cdot size(v))\cdot w dp[v]=dp[u]+(n−2⋅size(v))⋅w
Code
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
#define maxn 100005
struct BigInt
{
const static int mod=10000;
const static int LEN=4;
int a[10],len;
BigInt()
{
memset(a,0,sizeof(a));
len=1;
}
void init(ll x)
{
memset(a,0,sizeof(a));
len=0;
do
{
a[len++]=x%mod;
x/=mod;
}while(x);
}
void Init(const char s[])
{
memset(a,0,sizeof(a));
int l=strlen(s),res=0;
len=l/LEN;
if(l%LEN)len++;
for(int i=l-1;i>=0;i-=LEN)
{
int t=0,k=max(i-LEN+1,0);
for(int j=k;j<=i;j++)t=t*10+s[j]-'0';
a[res++]=t;
}
}
int Compare(const BigInt &b)
{
if(len<b.len)return -1;
if(len>b.len)return 1;
for(int i=len-1;i>=0;i--)
if(a[i]<b.a[i])return -1;
else if(a[i]>b.a[i])return 1;
return 0;
}
BigInt operator +(const BigInt &b)const
{
BigInt ans;
ans.len=max(len,b.len);
for(int i=0;i<=ans.len;i++)ans.a[i]=0;
for(int i=0;i<ans.len;i++)
{
ans.a[i]+=((i<len)?a[i]:0)+((i<b.len)?b.a[i]:0);
ans.a[i+1]+=ans.a[i]/mod;
ans.a[i]%=mod;
}
if(ans.a[ans.len]>0)ans.len++;
return ans;
}
BigInt operator -(const BigInt &b)const
{
BigInt ans;
ans.len=len;
int k=0;
for(int i=0;i<ans.len;i++)
{
ans.a[i]=a[i]+k-b.a[i];
if(ans.a[i]<0)ans.a[i]+=mod,k=-1;
else k=0;
}
while(ans.a[ans.len-1]==0&&ans.len>1)ans.len--;
return ans;
}
BigInt operator *(const BigInt &b)const
{
BigInt ans;
for(int i=0;i<len;i++)
{
int k=0;
for(int j=0;j<b.len;j++)
{
int temp=a[i]*b.a[j]+ans.a[i+j]+k;
ans.a[i+j]=temp%mod;
k=temp/mod;
}
if(k!=0)ans.a[i+b.len]=k;
}
ans.len=len+b.len;
while(ans.a[ans.len-1]==0&&ans.len>1)ans.len--;
return ans;
}
BigInt operator /(const int &n)const
{
BigInt ans;
ans.len=len;
int k=0;
for(int i=ans.len-1;i>=0;i--)
{
k=k*mod+a[i];
ans.a[i]=k/n;
k=k%n;
}
while(ans.a[ans.len-1]==0&&ans.len>1)ans.len--;
return ans;
}
void output()
{
printf("%d",a[len-1]);
for(int i=len-2;i>=0;i--)
printf("%04d",a[i]);
printf("\n");
}
};
vector<P>e[maxn];
int T,Case=1,n,Size[maxn];
ll f[maxn],g[maxn];
void dfs1(int u,int fa)
{
Size[u]=1;
f[u]=0;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i].first,w=e[u][i].second;
if(v==fa)continue;
dfs1(v,u);
Size[u]+=Size[v];
f[u]+=f[v]+(ll)w*Size[v];
}
}
void dfs2(int u,int fa)
{
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i].first,w=e[u][i].second;
if(v==fa)continue;
g[v]=g[u]+(ll)w*(n-2*Size[v]);
dfs2(v,u);
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)e[i].clear();
for(int i=1;i<n;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
e[a].push_back(P(b,c));
e[b].push_back(P(a,c));
}
dfs1(1,0);
g[1]=f[1];
dfs2(1,0);
sort(g+1,g+n+1);
BigInt ans,a,b;
ans.init(0);
for(int i=1;i<n;i++)
{
a.init(g[i]);
b.init(n-i);
ans=ans+a*b;
}
printf("Case #%d: ",Case++);
ans.output();
}
return 0;
}