2021南京H
题目链接
题目大意:
给定一棵树,树上每个节点都有一些蝴蝶,数量为
a
i
a_i
ai, 你可以去取走每个点的蝴蝶(从根节点1开始),但是当到达节点u后,点u的子节点会受到惊吓,会在
t
i
t_i
ti秒后离开,问最多可以取走多少蝴蝶。
树形DP,状态的表示和转移有点不太好想,参考了其他博客,这里记录一下其做法
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
//head
const int N=2e5+10;
int h[N],e[N],ne[N],idx;
int a[N],t[N];
int f[N],g[N];
//g[u]表示不选u的孩子得到的最大值,即取完u立即返回
//f[u]表示以u为根的子树得到的最大值
//假设v是u的孩子,
//则g[u]+=f[v]-a[v];
//u没有子节点,f[u]=g[u],若有子节点,f[u]=max(f[u],g[u]+a[j]),表示向哪个儿子走
//如果t[j]==3,说明可以先走向一个其他儿子z,再回来取j,这时z的贡献变为了g[z]。
//因为g[u]中已经累加过f[z]-a[z],所以要减去该值,所以最终z的贡献为g[z]-(f[z]-a[z])
//因为j和z可能为同一点,因此求出该贡献的最大值与次大值,来更新f[u]
void add(int a,int b)
{
e[idx]=b; ne[idx]=h[a]; h[a]=idx++;
}
void dfs(int u,int fa)
{
f[u]=g[u]=a[u];
int max1=-1e18,max2=-1e18;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa) continue;
dfs(j,u);
g[u]+=f[j]-a[j];
int x=g[j]-(f[j]-a[j]);
if(x>max1){
max2=max1; max1=x;
}
else if(x>max2) max2=x;
}
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa) continue;
f[u]=max(f[u],g[u]+a[j]);
if(t[j]==3){
if(g[j]-(f[j]-a[j])==max1) f[u]=max(f[u],g[u]+max2+a[j]);
else f[u]=max(f[u],g[u]+max1+a[j]);
}
}
}
void work()
{
int n;
cin>>n;
idx=0;
for(int i=1;i<=n;i++) h[i]=-1;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>t[i];
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b); add(b,a);
}
dfs(1,-1);
cout<<f[1]<<endl;
}
signed main()
{
ios;
int t;
cin>>t;
while(t--)
{
work();
}
return 0;
}
2021上海
题目链接
题目大意:
给定一个n个节点的无向图(n为奇数),有n-1条边,(其实就是一棵树)。
把这些边分组,要求如下:
- 每组有且仅有两条边。
- 在同一组的两条边有一个公共点
求共有多少种方案.
思路:
考察以x为根节点的子树的方案数,其中fa为x的父节点.按子树的节点数为奇数、偶数分为两种情况,分别称为I型、II型子树.
II型子树有偶数个节点,奇数条边,再加上边(x,y2)即可两两配对;I型子树有奇数个节点,偶数条边,y1的子树中的边两两配对后,边(x,y1)未配对,可能需与边(fa,x)配对,是否需要用到边(fa,x)取决于x的子树中I型子树的数量的奇偶:①有偶数个时,所有I型子树的边(x, y i y_i yi)可两两配对;②有奇数个时,所有I型子树的边(x, y i y_i yi)两两配对,剩下的一条边与(fa,x)配对.
参考的博客
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
//head
const int N=2e5+10,mod=998244353;
int e[N],ne[N],h[N],idx;
int sz[N],f[N];
int g[N];
void add(int a,int b)
{
e[idx]=b; ne[idx]=h[a]; h[a]=idx++;
}
void dfs(int u,int fa)
{
sz[u]=1; f[u]=1;
int cnt=0;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa) continue;
dfs(j,u);
sz[u]+=sz[j];
f[u]=f[u]*f[j]%mod;
if(sz[j]&1) cnt++;
}
if(cnt&1) f[u]=f[u]*g[cnt+1]%mod;
else f[u]=f[u]*g[cnt]%mod;
}
signed main()
{
ios;
int n;
cin>>n;
memset(h,-1,sizeof h);
g[0]=1;
for(int i=2;i<=n;i+=2){
g[i]=g[i-2]*(i-1)%mod;
}
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b); add(b,a);
}
dfs(1,-1);
cout<<f[1]<<endl;
return 0;
}