树形DP之王 balabalabala
Posted TSOI_Vergil
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树形DP之王 balabalabala相关的知识,希望对你有一定的参考价值。
题目:给你一棵树,要你求最大匹配和最大匹配的方案数。最大匹配就是选尽量多的边,使这些边的端点不重合。方案数要对1e9+7取模。n<=100000
这道题大概是我见过的树形DP中最难的,我称之为树P之王,首先,对于最大匹配,我们可以将边上放到点,即dp[i][0]表示不选i下面所连的边,dp[i][1]表示选i下面的边,那么我们可以知道dp[i][0]=西格玛max(dp[son[i]][1],dp[son[i]][0]),但是dp[i][1]就不好弄了,因为我们要找一个dp[son[i]][0]与i这个点连接,剩下的点所连的边就可选可不选了,那我们怎么保证最优呢?我们可以先将所有边变成可选可不选,然后再枚举每一条边,把它变成不选,得到一个匹配数,最后取一个max作为dp[i][1]的值。这样第1问我们就解决了。
而第二问才是最难的地方,我们容易想到用sum[i][0]和sum[i][1]分别表示i这个点选与不选时取最优值的方案数,sum[i][0]也比较好求,就是讨论一下dp[son[i]][0]和dp[son[i]][1]的关系,而sum[i][1]就比较复杂,因为dp[i][1]的转移是比dp[i][0]的转移复杂的,对于sum[i][1]的求法,有两种不同的做法,一种是我的,一种是APT的,先说我的做法,我是用了vector存了边,然后我们可以用两个辅助数组fz1[i]和fz2[i],其中fz1[i]表示方案数的前缀积,fz2[i]表示方案数的后缀积,那么对于一个可以被i连的son[i],设它在vector的位置是j,那么son[i]对答案的贡献为fz1[j-1]*fz2[j+1]*sum[son[i]][0],我们只需要统计每个可行的son[i]的和即可。再说说APT的做法,没有用vector,根据乘法原理和加法原理,对于每个可行的son[i],我们把先乘它应该乘的,再加上dp[son[i]][0],这样的话根据乘法原理算出来的答案是和用后缀积是一样的,但是这种做法速度快,但也不易理解,估计这个题做出来以后树形DP的题就不怕了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 100005
const long long mod=1000000007;
int T,opt,que[maxn],father[maxn],list[maxn];
int n,dp[maxn][2];
long long sum[maxn][2],fz1[maxn],fz2[maxn];
bool vis[maxn],flag[maxn],leaf[maxn];
vector<int> g[maxn];
void connect(int x,int y)
g[x].push_back(y);
void bfs(void)
memset(vis,0,sizeof vis);
int h=1,t=1;
que[1]=1;vis[1]=1;
while (h<=t)
int u=que[h];h++;
leaf[u]=1;
int siz=g[u].size();
for (int j=0;j<siz;j++)
int v=g[u][j];
if (vis[v]) continue;
leaf[u]=0;
father[v]=u;
que[++t]=v;
vis[v]=1;
int main()
freopen("hungary.in","r",stdin);
freopen("hungary.out","w",stdout);
scanf("%d%d",&T,&opt);
while (T--)
memset(dp,0,sizeof dp);
memset(fz1,0,sizeof fz1);
memset(fz2,0,sizeof fz2);
memset(leaf,0,sizeof leaf);
scanf("%d",&n);
for (int i=1;i<=n;i++) g[i].clear();
for (int i=1;i<n;i++)
int a,b;
scanf("%d%d",&a,&b);
connect(a,b);connect(b,a);
bfs();
for (int i=n;i>=1;i--)
int u=que[i];
int ret=0;
sum[u][0]=sum[u][1]=1;
int siz=g[u].size();
for (int j=0;j<siz;j++)
int v=g[u][j];
if (v==father[u]) continue;
dp[u][0]+=max(dp[v][1],dp[v][0]);
if (!leaf[v]&&dp[v][1]==dp[v][0]) sum[u][0]=(sum[u][0]*(sum[v][1]+sum[v][0]))%mod;
if (!leaf[v]&&dp[v][1]>dp[v][0]) sum[u][0]=(sum[u][0]*sum[v][1])%mod;
if (!leaf[v]&&dp[v][1]<dp[v][0]) sum[u][0]=(sum[u][0]*sum[v][0])%mod;
ret+=max(dp[v][0],dp[v][1]);
fz1[j]=sum[u][0];
long long temp=1;
for (int j=siz-1;j>=0;j--)
int v=g[u][j];
if (v==father[u]) continue;
if (!leaf[v]&&dp[v][1]==dp[v][0]) temp=(temp*(sum[v][1]+sum[v][0]))%mod;
if (!leaf[v]&&dp[v][1]>dp[v][0]) temp=(temp*sum[v][1])%mod;
if (!leaf[v]&&dp[v][1]<dp[v][0]) temp=(temp*sum[v][0])%mod;
fz2[j]=temp;
for (int j=0;j<siz;j++)
int v=g[u][j];
if (v==father[u]) continue;
dp[u][1]=max(dp[u][1],ret-max(dp[v][0],dp[v][1])+dp[v][0]+1);
temp=0;fz2[siz]=1;
if (fz1[0]==0) fz1[0]=1;
for (int j=0;j<siz;j++)
int v=g[u][j];
if (v==father[u]) continue;
if (dp[u][1]==ret-max(dp[v][0],dp[v][1])+dp[v][0]+1)
if (j==0) temp=(temp+fz2[j+1]*sum[v][0])%mod;
else temp=(temp+((fz1[j-1]*fz2[j+1])%mod*sum[v][0])%mod)%mod;
sum[u][1]=temp;
if (sum[u][1]==0) sum[u][1]=1;
if (opt==1) printf("%d\\n",max(dp[1][1],dp[1][0]));
else
if (dp[1][1]>dp[1][0]) printf("%d %I64d\\n",dp[1][1],sum[1][1]);
if (dp[1][0]>dp[1][1]) printf("%d %I64d\\n",dp[1][0],sum[1][0]);
if (dp[1][0]==dp[1][1]) printf("%d %I64d\\n",dp[1][1],(sum[1][0]+sum[1][1])%mod);
fclose(stdin);
fclose(stdout);
return 0;
以上是关于树形DP之王 balabalabala的主要内容,如果未能解决你的问题,请参考以下文章