BZOJ2159:Crash 的文明世界 (第二类stirling数+组合数学+树形DP)

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2159


题目分析:先说一下部分分怎么拿。

20%:直接以每个点为根DFS一遍,求出其它点的深度。预处理1k~nk统计答案。时间复杂度O(n2+nk)

50%:令S[node][d]=unodedis(node,u)d。当计算node父亲节点fa的S值时,显然有:

S[fa][d]=p=0dCdpS[node][p]

第一遍DFS完之后,再用一次DFS计算node的祖先对node的贡献。这个套路很常见,就不再多说了。时间复杂度是O(nk2)

100%发现计算S[fa][d]是个卷积的形式,所以用NTT优化

上面的方法虽然时间是O(nklog(k))的,但绝对会T。

从最初的式子考虑,我们发现dis(i,j)k是个幂,于是考虑用第二类stirlng数将其代换:

dk=p=0dCdpS(k,p)(p!)

其中S(k,p)是第二类stirling数。由于当p>kS(k,p)值为0,d的上界还可以跟k取个min。S(k,p)可以用递推式S(i,j)=S(i1,j1)+S(i1,j)j求,也可以用容斥+NTT算一行。不过因为k很小,所以直接递推即可。由于k是定值,为了方便直接记h[p]=S(k,p)(p!)

现在我们主要关注Cdp这一项。不妨令f[node][p]表示unodeCdis(node,u)p。考虑计算node的父亲fa的f值,发现node子树中的所有点距离+1。根据杨辉三角Cd+1p=Cdp+Cdp1,可以算出f[node][p1]+f[node][p]f[fa][p]有贡献。同样用两次DFS即可求出所有答案。

一开始我想这题的时候忘了杨辉三角的公式,然后发现从Cdp转移到Cd+1p很困难,以为要对不同的d分开处理,然后就走远了。以后要多做些组合数学的题才行QAQ。


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=50100;
const int maxk=160;
const int M=10007;

struct edge
{
    int obj;
    edge *Next;
} e[maxn<<1];
edge *head[maxn];
int cur=-1;

int f[maxn][maxk];
int g[maxn][maxk];

int S[maxk][maxk];
int h[maxk];
int fac[maxk];

int fa[maxn];
int n,k;

void Add(int x,int y)
{
    cur++;
    e[cur].obj=y;
    e[cur].Next=head[x];
    head[x]=e+cur;
}

void Dfs1(int node)
{
    f[node][0]=1;
    for (edge *p=head[node]; p; p=p->Next)
    {
        int son=p->obj;
        if (son==fa[node]) continue;
        fa[son]=node;
        Dfs1(son);

        f[node][0]=(f[node][0]+f[son][0])%M;
        for (int i=1; i<=k; i++)
            f[node][i]=(f[node][i]+f[son][i]+f[son][i-1])%M;
    }
}

void Dfs2(int node)
{
    for (edge *p=head[node]; p; p=p->Next)
    {
        int son=p->obj;
        if (son==fa[node]) continue;

        g[son][0]=f[node][0]-f[son][0];
        for (int i=0; i<=k; i++)
            g[son][i]=(f[node][i]-f[son][i]-f[son][i-1]+2*M)%M;
        for (int i=0; i<=k; i++) g[son][i]=(g[son][i]+g[node][i])%M;
        for (int i=k; i>=1; i--) g[son][i]=(g[son][i]+g[son][i-1])%M;

        Dfs2(son);
    }
}

int main()
{
    freopen("2159.in","r",stdin);
    freopen("2159.out","w",stdout);

    int L,now,A,B,Q,tmp;
    scanf("%d%d%d",&n,&k,&L);
    for (int i=1; i<=n; i++) head[i]=NULL;
    scanf("%d%d%d%d",&now,&A,&B,&Q);
    for (int i=1; i<n; i++)
    {
        now=(now*A+B)%Q;
        tmp=((i<L)?i:L);
        int x=i-now%tmp,y=i+1;
        Add(x,y);
        Add(y,x);
    }

    for (int i=1; i<=k; i++) S[i][1]=1;
    for (int i=2; i<=k; i++)
        for (int j=2; j<=i; j++)
            S[i][j]=(S[i-1][j-1]+S[i-1][j]*j)%M;
    fac[0]=1;
    for (int i=1; i<=k; i++) fac[i]=fac[i-1]*i%M;
    for (int i=1; i<=k; i++) h[i]=S[k][i]*fac[i]%M;

    Dfs1(1);
    Dfs2(1);

    for (int i=1; i<=n; i++)
    {
        int ans=0;
        for (int j=0; j<=k; j++) ans=(ans+ (f[i][j]+g[i][j])*h[j] )%M;
        printf("%d\n",ans);
    }

    return 0;
}
发布了160 篇原创文章 · 获赞 76 · 访问量 10万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览