【比赛记录】树形dp专项测试1

A. Promises I Can't Keep
题目意为求以每个点为根时的期望得分的最大值,换根DP即可。
式子不太难推,半个小时就出来了。太长了不往这写了。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=5e5+5;
int n,cnt[maxn];
double a[maxn],f[maxn];
vector<int> e[maxn];
il void dfs1(int u,int fa){
	f[u]=a[u];
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs1(v,u);
		cnt[u]++;
	}
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		f[u]+=f[v]/cnt[u];
	}
}
il void dfs2(int u,int fa){
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		double tmp=cnt[u]==1?a[u]:(((f[u]-a[u])*cnt[u]-f[v])/(cnt[u]-1)+a[u]);
		f[v]=a[v]+((f[v]-a[v])*cnt[v]+tmp)/(cnt[v]+1);
		cnt[v]++;
		dfs2(v,u);
	}
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n);
	for(int i=1,u,v;i<n;i++){
		read(u)read(v);
		e[u].pb(v),e[v].pb(u);
	}
	for(int i=1;i<=n;i++){
		scanf("%lf",a+i);
	}
	dfs1(1,0),dfs2(1,0);
	double ans=0;
	for(int i=1;i<=n;i++){
		ans=max(ans,f[i]);
	}
//	for(int i=1;i<=n;i++){
//		cout<<f[i]<<" ";
//	}
//	puts("");
	printf("%.4f",ans);
	return 0;
}
}
int main(){return asbt::main();}
/*
4
1 2
2 3
2 4
1 2 3 4
*/

刚这个是考场代码,直接0分。
原因是:

接下来电流会等概率且不重复经过一个点地流向一个叶子节点

一个叶子节点,不是一个子节点。。。
正解在这里

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=5e5+5;
int n,a[maxn],cnt[maxn],tot;
long double f[maxn];
vector<int> e[maxn];
il void dfs1(int u,int fa){
	f[u]=a[u];
	if(fa&&e[u].size()==1){
		cnt[u]=1;
	}
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs1(v,u);
		cnt[u]+=cnt[v];
	}
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		f[u]+=f[v]*cnt[v]/cnt[u];
	}
}
il void dfs2(int u,int fa){
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		double tmp=(((f[u]-a[u])*cnt[u]-f[v]*cnt[v]))/(tot-cnt[v])+a[u];
		tmp=(f[v]-a[v])*cnt[v]+tmp*(tot-cnt[v]);
		cnt[v]=tot;
		if(e[v].size()==1){
			cnt[v]--;
		}
		f[v]=tmp/cnt[v]+a[v];
		dfs2(v,u);
	}
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n);
	for(int i=1,u,v;i<n;i++){
		read(u)read(v);
		e[u].pb(v),e[v].pb(u);
	}
	for(int i=1;i<=n;i++){
		read(a[i]);
		if(e[i].size()==1){
			tot++;
		}
	}
	dfs1(1,0);
//	for(int i=1;i<=n;i++){
//		cout<<cnt[i]<<" ";
//	}
//	puts("");
	dfs2(1,0);
//	for(int i=1;i<=n;i++){
//		cout<<f[i]<<" ";
//	}
//	puts("");
	long double ans=-1e18;
	for(int i=1;i<=n;i++){
		ans=max(ans,f[i]);
	}
	printf("%.4Lf",ans);
	return 0;
}
}
int main(){return asbt::main();}
/*
5
1 2
2 3
3 4
4 5
1 2 3 4 5
*/

B. [COCI2016-2017#4] Rima
考场上想到了倒着建trie,也想到了父亲和儿子只有两种情况,但是忽略了一个很重要的性质,那就是因为没有两个相同的字符串,所以最后形成的字符串序列一定是一个长度先减后增的样子。
所以可以在trie树上搞一个很简单的DP,设 \(dp[u]\) 表示以 \(u\) 为结尾的字符串在一端的最长序列,于是当 \(u\) 是一个结尾,且它的儿子也是一个结尾时,就可以转移。
最终的答案一定是先减后增的情况,式子也不难推。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
	char ch;\
	int fu=1;\
	while(!isdigit(ch=getchar()))\
		fu-=(ch=='-')<<1;\
	x=ch&15;\
	while(isdigit(ch=getchar()))\
		x=(x<<1)+(x<<3)+(ch&15);\
	x*=fu;\
}
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=3e6+5;
int n,tot,dp[maxn],end[maxn];
vector<pair<int,char> > e[maxn];
char s[maxn];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		scanf(" %s",s+1);
		int len=strlen(s+1);
		int p=0;
		for(int j=len;j;j--){
			for(auto x:e[p]){
				if(x.sec==s[j]){
					p=x.fir;
					goto togo;
				}
			}
			e[p].pb(mp(++tot,s[j]));
			p=tot;
			togo:;
		}
		end[p]++;
	}
	int ans=0;
	for(int u=tot;~u;u--){
		int mx1=0,mx2=0,cnt=0;
		for(auto x:e[u]){
			int v=x.fir;
			cnt+=end[v];
			if(dp[v]>mx1){
				mx2=mx1;
				mx1=dp[v];
			}
			else{
				mx2=max(mx2,dp[v]);
			}
		}
		if(end[u]){
			dp[u]=mx1+max(cnt,1);
		}
		ans=max(ans,mx1+mx2+max(cnt-2,0)+end[u]);
	}
	printf("%d",ans);
	return 0;
}
}
int main(){return asbt::main();}

C. [CEOI2020] 星际迷航
之前就出过一次这道题,当时改过了,但是那道题数据太水了,所以同一篇代码拿到这里直接获得7pts……
先咕着吧

D. 「SMOI-R1」Company
其实很简单。设 \(a_i\) 为第 \(i\) 棵树的根为最后的根时对答案的最大贡献,\(b_i\) 为第 \(i\) 棵树不为根时对答案的最大贡献。那么答案为

\[\begin{align} &\max_{i=1}^n(a_i+\sum_{j=1\land j\ne i}^nb_j)\\ =&\max_{i=1}^n(a_i-b_i)+\sum_{i=1}^nb_i \end{align} \]

对于 \(i=1\)\(i=2\),显然 \(b_i\)\(x\)\(y\) 的深度,而 \(a_i\) 为离 \(x\)\(y\) 最远的叶子节点的距离。
对于 \(i\ge 3\)\(b_i\) 为最深的叶子节点的深度,\(a_i\) 为相距最远的两个叶子节点的距离。
树形DP即可。
然而实在是没时间写了,所以也咕着吧(逃

posted @ 2024-12-15 23:26  zhangxy__hp  阅读(20)  评论(0)    收藏  举报