Newcoder 144 G.Pikachu(最大流+树形DP+高精度)

本文探讨了一种特殊的最大流问题,通过树形DP算法求解任意两点间的最大流之和。首先介绍问题背景及输入输出格式,然后详细解析了解决方案,包括两遍树形DP的具体实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出一棵nnn个节点的树,边有边权,对这nnn个节点建一张完全图,图中两点间距离为两点的树上距离,问任意两点在图上的最大流之和

Input

第一行一整数TTT表示用例组数,每组用例首先输入一整数nnn表示点数,之后n−1n-1n1行每行输入三个整数u,v,wu,v,wu,v,w表示u,vu,vu,v之间有一条权值为www的树边

(1≤T≤10,2≤n≤105,1≤wi≤1000)(1\le T\le 10,2\le n\le 10^5,1\le w_i\le 1000)(1T10,2n105,1wi1000)

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,ts,ts,t两点之间的最大流即为求两点的最小割,进而是将nnn个点分成两个点集S,TS,TS,T,使得s∈S,t∈Ts\in S,t\in TsS,tT,且∑u∈S,v∈Tdis(u,v)\sum\limits_{u\in S,v\in T}dis(u,v)uS,vTdis(u,v)最小,以黑白染色来确定每点所属集合,为使两个集合之间的距离和最小,集合内的距离和应尽可能大,故除s,ts,ts,t外其余点应同色,故s,ts,ts,t之间最大流即为min(∑dis(u,i),∑dis(v,i))min(\sum dis(u,i),\sum dis(v,i))min(dis(u,i),dis(v,i)),只要求出一点到其余所有点的距离和即可

第一遍树形DPDPDP求出所有点到根节点的距离和,第二遍树形DPDPDPdp[u]dp[u]dp[u]表示所有点到uuu点距离和,假设根节点为111,那么dp[1]dp[1]dp[1]在第一遍时已经求出,进而对于当前点vvv,其父亲uuudpdpdp值已经确定,所有点中位于vvv子树的点到达uuu比到达vvv多走了wwwwwwu,vu,vu,v之间边权),其余点到达uuu比到达vvv少走了www,故有转移

dp[v]=dp[u]+(n−2⋅size(v))⋅wdp[v]=dp[u]+(n-2\cdot size(v))\cdot wdp[v]=dp[u]+(n2size(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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值