label {树形dp+机智的优化}

来源:互联网 发布:淘宝等级贷款 编辑:程序博客网 时间:2024/04/28 04:56
  • 【题目描述】
    Samjia和Peter不同,他喜欢玩树。所以Peter送给他一颗大小为n的树,节点编号从1到n。
    Samjia要给树上的每一个节点赋一个[1,m]之间的权值,并使得有边直接相连的两个节点的权值之差的绝对值 ≥ k。请你告诉Samjia有多少种不同的赋值方案,只用求出答案对10^9+7(1000000007)取模得到的结果。

  • 【Sample Input】
    (输入数据的第一行包含一个整数 T,测试数据组数。
    每组数据的第一行包含三个整数 n、m 和 k。
    接下来 n − 1 行,每行包含两个整数 u 和 v, 代表节点 u 和 v 之间有一条树边。)
    3
    2 2 0
    1 2
    3 3 2
    1 3
    1 2
    3 3 1
    1 2
    2 3

  • 【Sample Output】
    (对于每组数据输出一个整数,代表所求的答案。)
    4
    2
    12

  • 【数据范围】
    测试点编号 m≤ 特殊约定
    1,2 100 无
    3,4 10000 无
    5,6 10^9 第2-n号节点与1号节点直接相连
    7,8 10^9 第i号节点与第i+1号节点直接相连
    9,10 10^9 无


【题解】题目看完知道是树形dp

{30%} 裸的随便写,暴力方程:f[i][j]=∏(sigma(f[son[i]][l]))
f[i][j] 表示节点 i 的数值为 j 时的方案数,abs(l-j)<=k;

{60%} 在上式的基础上,对于每个儿子维护前缀和即可;

{100%} 如果我们仔细观察,可以发现一个性质:

    对于每个节点x,对于f[x]的中间某一段值可能会出现重复,而重复值段旁的前后两段是对称的。
   
    证明的话,简单的来看,对于叶子节点的每个值答案都是1。那么其父亲累加累乘的时候,到中间就会出现一段相等的值,因为这些值所覆盖的儿子的值域对应的方案数是相等的,而前后两段累加累乘计算的值相同 //貌似怎么解释都有点别扭。。。
    所以这些相同的点我们只需要记录其中一个。接下来考虑,不同的点最多有多少个?
    从叶子节点往上推,每层最多比上层多 k 个,所以对于任意节点,最多有 (n-1)*k 个不同的值。
    //蒟蒻→QAQ个人到了这里其实还是不会打。。。

思路和写法整理一下
(1) 因为对于每个点有很多相同的,要记录的就是取值范围在:max((n-1)*k,m) 的方案。
(2) 计算每个节点时,先求出当前点 x 取值为1时儿子可取值的和sum{就是一个后缀},随着 x 取值的增大修改sum并计算出当前取值的方案数。具体还是看程序。


#include <cstdio>#include <iostream>#include <cstring> #define LL long long#define mo 1000000007struct edge{ int to,nxt;}e[205];int T,n,m,k,l,cnt,f[105][100005],fi[105];     void add(int u,int v)    {        e[++cnt].to=v;e[cnt].nxt=fi[u];fi[u]=cnt;    }    LL qsm(int x,int y)    {        if (y==1) return x;        LL t=qsm(x,y>>1);        if (y&1) return t*t%mo*x%mo;        else return t*t%mo;    }    LL sum(int x,int y)    {        LL s=0;        for (int i=y;i<=l;++i) (s+=f[x][i])%=mo;        for (int i=m;i>m-l && i>l && i>=y;--i) (s+=f[x][m-i+1])%=mo;        int u,v;        u=std::max(l+1,y);v=m-l;        if (u<=v) (s+=(1ll*f[x][l]*(v-u+1))%mo)%=mo;        return s;     }    void dfs(int x,int fa)    {        for (int i=1;i<=l;++i) f[x][i]=1;        for (int i=fi[x];i;i=e[i].nxt)            if (e[i].to!=fa)            {                dfs(e[i].to,x);                int p=sum(e[i].to,k+1);                for (int j=1;j<=l;++j)                {                    if (j>k) (p+=f[e[i].to][j-k])%=mo;                    (p+=mo)%=mo;                    f[x][j]=1ll*p*f[x][j]%mo;                    if (j+k<=m)                    {                        if (j+k<=l) (p-=f[e[i].to][j+k])%=mo;                        else if (j+k>m-l) (p-=f[e[i].to][m-j-k+1])%=mo;                        else (p-=f[e[i].to][l])%=mo;                    }                }            }    }int main(){    for (scanf("%d\n",&T);T;--T)    {        scanf("%d%d%d\n",&n,&m,&k);        memset(fi,0,sizeof(fi));cnt=0;        for (int i=1;i<n;++i)        {            int u,v;scanf("%d%d\n",&u,&v);            add(u,v);add(v,u);        }        if (!k) printf("%lld\n",qsm(m,n));        else         {            l=std::min((n-1)*k,m);            dfs(1,0);            printf("%lld\n",sum(1,1));        }    }    return 0;}

【题外话】每次打题都会有一些小毛病,有aufeas战god在真是太好了  QAQ  QwQ

0 0
原创粉丝点击