[vijos1892]树上的最大匹配(树形DP)
2014-10-19 22:59
375 查看
题目:https://vijos.org/p/1892
分析:(100分其实用到各种c++优化,没什么实际意义,所以弄70就可以了)
题目很简单,很容易想出用树形DP,但是求方案数的时候,满满都是细节……,本渣考试时候就跪了……只能膜拜神犇代码……
View Code
细节反思:
1、求f和求g的过程可以一块写,思路比较清晰一点
2、求g[u][1]的时候的技巧:
本渣只能想到先求所有的乘积,然后再枚举每一个位置的,除掉,因为取模只能求逆
但此神犇的做法很厉害:
先在求f的过程中把u的每个子节点的最优值记下来保存在数组中,并记下来u往叶子节点连边能得到的最大增值maxt
然后把记最优值的数组从前往后累乘得到pre,从后往前乘得到suf
然后对于每次枚举的连边的子节点i,首先判断连i所能得到的增值是否为maxt,如果是那么增加的方案数也就确定了:pre[i-1]*suf[i+1]*g[i][0]
细节方面真的很重要……
分析:(100分其实用到各种c++优化,没什么实际意义,所以弄70就可以了)
题目很简单,很容易想出用树形DP,但是求方案数的时候,满满都是细节……,本渣考试时候就跪了……只能膜拜神犇代码……
#include <cstdio> #include <cstring> //#include <algorithm> using namespace std; typedef long long LL; const int MaxN = 1500010; struct Node{ int v; Node *nxt; }pool[MaxN << 1],*tail=pool,*g[MaxN]; int n; LL m; LL h[MaxN][2]; int fa[MaxN],f[MaxN][2]; LL pre[MaxN],suf[MaxN]; inline void make_edge(int u,int v){ tail->v=v;tail->nxt=g[u];g[u]=tail++; tail->v=u;tail->nxt=g[v];g[v]=tail++; } inline int max(int a,int b){return a>b ? a : b;} void dp(){ static int q[MaxN],l,r; memset(fa,0xff,sizeof(fa)); for(fa[q[l=r=0]=1]=0;l<=r;l++) for(Node *p=g[q[l]];p;p=p->nxt) if(!~fa[p->v]) fa[q[++r]=p->v]=q[l]; for(int i=r;i>=0;i--){ int u=q[i]; int maxt=0xc0c0c0c0; int cnt=0,j; f[u][0]=0,f[u][1]=0; h[u][0]=1,h[u][1]=0; for(Node *p=g[u];p;p=p->nxt) if(p->v!=fa[u]){ LL dt=0; f[u][0]+=max(f[p->v][0],f[p->v][1]); f[u][1]+=max(f[p->v][0],f[p->v][1]); maxt=max(maxt,f[p->v][0]+1-max(f[p->v][0],f[p->v][1])); if(f[p->v][0]>f[p->v][1]) dt=h[p->v][0]; else if(f[p->v][0]<f[p->v][1]) dt=h[p->v][1]; else dt=(h[p->v][0]+h[p->v][1])%m; pre[++cnt]=dt;suf[cnt]=dt; (h[u][0]*=dt)%=m; } pre[0]=suf[cnt+1]=1; for(int i=1;i<=cnt;i++) (pre[i]*=pre[i-1])%=m; for(int i=cnt;i;i--) (suf[i]*=suf[i+1])%=m; f[u][1]+=maxt; j=1; for(Node *p=g[u];p;p=p->nxt) if(p->v!=fa[u]){ if(f[p->v][0]+1-max(f[p->v][0],f[p->v][1])==maxt) (h[u][1]+=pre[j-1]*suf[j+1]%m*h[p->v][0]%m)%=m; j++; } } if(f[1][0]==f[1][1]) printf("%d\n%lld\n",f[1][0],(h[1][0]+h[1][1])%m); else if(f[1][0]>f[1][1]) printf("%d\n%lld\n",f[1][0],h[1][0]); else printf("%d\n%lld\n",f[1][1],h[1][1]); } int main() { scanf("%d",&n); for(int i=1;i<n;i++){ int u,v;scanf("%d%d",&u,&v); make_edge(u,v); } scanf("%lld",&m); dp(); return 0; }
View Code
细节反思:
1、求f和求g的过程可以一块写,思路比较清晰一点
2、求g[u][1]的时候的技巧:
本渣只能想到先求所有的乘积,然后再枚举每一个位置的,除掉,因为取模只能求逆
但此神犇的做法很厉害:
先在求f的过程中把u的每个子节点的最优值记下来保存在数组中,并记下来u往叶子节点连边能得到的最大增值maxt
然后把记最优值的数组从前往后累乘得到pre,从后往前乘得到suf
然后对于每次枚举的连边的子节点i,首先判断连i所能得到的增值是否为maxt,如果是那么增加的方案数也就确定了:pre[i-1]*suf[i+1]*g[i][0]
细节方面真的很重要……
相关文章推荐
- (vijos 1892 noip 模拟 tree)<树形DP求树的最大匹配及方案数>
- 【vijos】1892 树上的最大匹配(树形dp+计数)
- 17AHU排位赛2 E题(树上最大匹配,树形DP)
- Vijos p1892 树上的最大匹配 树形DP+计数 被卡常我有特殊技巧heheda
- vijos 1892 树上的最大匹配问题 树形dp
- HDU 5242Game 树上的贪心 树形dp 求出使K条链的权值总和最大
- 树形DP 或 最小顶点覆盖=最大匹配(双向图)(HDU 1053)
- uva 1220 ,Patty at Hali-Bula 树形dp 树上最大独立集 并判断是否唯一
- 树上的 DP - 求树的最大匹配数
- hdu Strategic Game 二分图最大匹配/树形DP
- Tsinsen A1320 Painting 【最大权匹配】【树形DP】
- 区间DP入门题--最大匹配的括号数
- Vijos 1144 小胖守皇宫 【树形DP】
- 树形dp ,求最大路径权值,最长路径
- hdu 2196 Computer 树形dp 树中点最大距离
- vijos1212 Way Selection(二分图最大匹配)
- bzoj 4033: [HAOI2015]树上染色(树形DP)
- vijos1144&&bzoj1596(树形dp,树上最小控制集
- 树形DP 树的最小支配集,最小点覆盖与最大独立集
- hdu1054 【树形dp】【二分匹配】