分析:(100分其实用到各种c++优化,没什么实际意义,所以弄70就可以了)
题目很简单,很容易想出用树形DP,但是求方案数的时候,满满都是细节……,本渣考试时候就跪了……只能膜拜神犇代码……
#include <cstdio>
#include <cstring>
//#include <algorithm> using namespace std; typedef long long LL; const int MaxN = ; struct Node{
int v;
Node *nxt;
}pool[MaxN << ],*tail=pool,*g[MaxN]; int n;
LL m;
LL h[MaxN][];
int fa[MaxN],f[MaxN][];
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=]=]=;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>=;i--){
int u=q[i];
int maxt=0xc0c0c0c0;
int cnt=,j;
f[u][]=,f[u][]=;
h[u][]=,h[u][]=;
for(Node *p=g[u];p;p=p->nxt) if(p->v!=fa[u]){
LL dt=;
f[u][]+=max(f[p->v][],f[p->v][]);
f[u][]+=max(f[p->v][],f[p->v][]);
maxt=max(maxt,f[p->v][]+-max(f[p->v][],f[p->v][])); if(f[p->v][]>f[p->v][]) dt=h[p->v][];
else if(f[p->v][]<f[p->v][]) dt=h[p->v][];
else dt=(h[p->v][]+h[p->v][])%m;
pre[++cnt]=dt;suf[cnt]=dt;
(h[u][]*=dt)%=m;
}
pre[]=suf[cnt+]=;
for(int i=;i<=cnt;i++) (pre[i]*=pre[i-])%=m;
for(int i=cnt;i;i--) (suf[i]*=suf[i+])%=m;
f[u][]+=maxt;
j=;
for(Node *p=g[u];p;p=p->nxt) if(p->v!=fa[u]){
if(f[p->v][]+-max(f[p->v][],f[p->v][])==maxt)
(h[u][]+=pre[j-]*suf[j+]%m*h[p->v][]%m)%=m;
j++;
}
}
if(f[][]==f[][]) printf("%d\n%lld\n",f[][],(h[][]+h[][])%m);
else if(f[][]>f[][]) printf("%d\n%lld\n",f[][],h[][]);
else printf("%d\n%lld\n",f[][],h[][]);
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
make_edge(u,v);
}
scanf("%lld",&m);
dp();
return ;
}
细节反思:
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]
细节方面真的很重要……