dp修炼(组合数的迭代&&树形dp求解任意树中两点间的距离和)

一:组合数迭代

B-Distance_2023牛客暑期多校训练营6 (nowcoder.com)

领悟了龙哥的代码,发现组合数迭代很奇妙,所以想记下来,怕遗忘:

题意:给定两个长度相等的数组,两个数组的数可以任选相同数量的两个数,求出所有可能的数组总差值的和。

从数据来说1e3,暴力支持循环套循环。在赛时的时候觉得是dp+组合数,但在考虑dp的时候,只顾着追求状态转移,而忘记了组合数的思路。

我们把第一个数组设为a数组,第二个设为b数组,设置二维dp[i][j],i表示a数组的前i项参与选取,j表示b数组的前j项参与选取。dp[i][j]用于迭代所有的可能和。转移方程位dp[i][j]=dp[i-1][j]+dp[i][j-1]

然后我们再次枚举,通过组合数算出答案。

代码如下:

#include
using namespace std;
typedef long long ll;
const int N=998244353;
ll n;
ll a[5000];
ll b[5000];
ll dp[2005][2005];
int main(){
	ios::sync_with_stdio(false);
	cin.tie();
	cout.tie();
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	
	for(int i=1;i<=n;i++)cin>>b[i];
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			dp[i][j]=(dp[i-1][j]+dp[i][j-1]+1)%N;
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			ans=(ans+abs(a[i]-b[j])%N*(dp[i-1][j-1]+1)%N*(dp[n-i][n-j]+1)%N)%N; 
		}
	}
	cout<

二:树形dp

Problem - C - Codeforces

本题考察了比较基础的树状dp与组合数。组合数+树形dp还是比较难的,个人认为这是个比较好的入门题。

题意如下:

赛义德和艾哈迈德打算去特里兰旅行,特里兰是一个有 n个旅游景点的城市,这些旅游景点之间的道路是特定的,这意味着只有当且仅当它们之间有道路时,你才能从一个旅游景点移动到另一个旅游景点、所有旅游景点之间总共有 n−1条道路,可以保证你可以通过有限的道路序列从任何一个旅游景点到达另一个旅游景点,即 n个旅游景点组成了一棵相连的树。

艾哈迈德和赛义德想以下面的方式游览所有的 n个旅游景点:

首先,他们将指定访问旅游景点的顺序,也就是说,他们将给每个旅游景点一个介于1和n之间的唯一编号。

其次,他们将从编号为1的旅游景点开始,前往编号为2的旅游景点,然后前往编号为3的旅游景点,依此类推,直到他们访问编号为n的旅游景点。

艾哈迈德很懒,走几步就会觉得累,所以赛义德想在他们出门之前向他提供行程的步数。问题是他们没有计划好去旅游景点的顺序,所以赛义德决定计算所有可能顺序的预期行程步数。

两个旅游景点之间的步数就是它们之间最短路径的路数。

题解:

首先可以用分治的思想:

算出每个相邻点儿的距离,然后算出这一段对于总和的贡献。公式还是很好推的,于是我们考虑重点,也就是求出任意两点的距离的总和。

代码如下:

#include
using namespace std;
typedef long long ll;
ll b[500005];
ll n;
int main(){
	ios::sync_with_stdio(false);
	cin.tie();
	cout.tie();
	int t;
	cin>>t;
	while(t--){
		
		cin>>n;	
		vector>ve(n+5);
		for(int i=1;i>l>>r;
			ve[l].push_back(r);
			ve[r].push_back(l);
		}
		vectordp(n+5);
		vectornn(n+5,1);
		ll ans=0;
		// ll bnn=0;

		auto dfs=[&](auto self,int fa,int x)->void{
			for(auto i:ve[x]){
				if(i==fa)continue;
				self(self,x,i);
				nn[x]+=nn[i];
			}
			if(fa!=0)ans+=dp[fa]*nn[x]+(nn[x]+dp[x])*nn[fa];
			dp[fa]+=nn[x]+dp[x];
			

		};
		dfs(dfs,0,1);

		double ann=2.000000*ans/n;
		printf("%.7lf\n",ann);
	}
}

你可能感兴趣的:(算法,c++)