loj3389. 「NOIP2020」微信步数

题目链接

首先判掉无穷大。

考虑让人分身到每一个点,然后分别统计每走一步活下来的人数。

不难发现除了第一轮,以后每一轮在某个维度上挂掉的人都是一样多的,因为每轮过后只有新增加的变化量的部分会挂掉。

于是先把第一轮单独处理了,后面每一轮其实都是固定的。

快速维护这个贡献。我们记第 \(i\) 维第一轮活下来的点数量是 \(a_i\),以后每轮会挂掉 \(b_i\) 个点,记 \(c_i\)为在第 \(i\) 步挂掉的点。

然后我们发现最大的轮数就是 \(\min_\limits{1\leq i\leq k}\lfloor\frac{a_i-c_{j}}{b_i}\rfloor\),记这个是 \(maxn\),最后一轮由于跑不完所以要枚举,那么我们相当于要求 \(\sum_\limits{i=1}^n\sum_\limits{x=0}^{maxn}\prod_\limits_{j=1}^k-b_jx+a_j-c_i\)

然后里面是个 \(k\) 次多项式,可以直接 \(O(m^2)\) 算出系数。然后我们把 \(\sum_\limts{i=0}^{maxn}i^m\) 依次带进每一项即可,复杂度 \(O(nm^2)\)

#include<iostream>
#include<cstdio>
using namespace std;
#define int long long
const int mod=1000000007;
int n,m,g[21],w[21],d[500001],l[500001][21],r[500001][21],ans,fac[500011],sum[500011][2],a[500001],b[500001],f[21][21];
inline int pw(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1)
            res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}
inline int solve(int n,int k)
{
    int res=0,tot=0;
    fac[0]=1;
    for(register int i=1;i<=k+2;++i)
        fac[i]=fac[i-1]*i%mod;
    sum[0][0]=sum[k+3][1]=1;
    for(register int i=1;i<=k+2;++i)
        sum[i][0]=sum[i-1][0]*((n-i+mod)%mod)%mod;
    for(register int i=k+2;i;--i)
        sum[i][1]=sum[i+1][1]*((n-i+mod)%mod)%mod;
    for(register int i=1;i<=k+2;++i)
    {
        tot=(tot+pw(i,k))%mod;
        res=(res+tot*sum[i-1][0]%mod*sum[i+1][1]%mod*pw(((k-i)&1? mod-fac[i-1]:fac[i-1])%mod*fac[k+2-i]%mod,mod-2)%mod)%mod;
    }
    return res;
}
inline int read()
{
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
signed main()
{
    n=read(),m=read();
    for(register int i=1;i<=m;++i)
        w[i]=read();
    for(register int i=1;i<=n;++i)
    {
        int x=read(),y=read();
        d[x]+=y;
        for(register int j=1;j<=m;++j)
        {
            l[i][j]=l[i-1][j];
            r[i][j]=r[i-1][j];
        }
        l[i][x]=min(l[i][x],d[x]);
        r[i][x]=max(r[i][x],d[x]);
    }
    bool flag=1;
    for(register int i=1;i<=m;++i)
        if(d[i]||r[n][i]-l[n][i]>w[i])
        {
            flag=0;
            break;
        }
    if(flag)
    {
        puts("-1");
        return 0;
    }
    for(register int i=0;i<=n;++i)
    {
        int tot=1;
        for(register int j=1;j<=m;++j)
            tot=tot*max(0ll,w[j]-r[i][j]+l[i][j])%mod;
        ans=(ans+tot)%mod;
    }
    for(register int i=1;i<=m;++i)
        a[i]=w[i]-r[n][i]+l[n][i];
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)
        {
            l[i][j]=min(0ll,l[i][j]+d[j]-l[n][j]);
            r[i][j]=max(0ll,r[i][j]+d[j]-r[n][j]);
        }
    for(register int i=1;i<=m;++i)
        b[i]=r[n][i]-l[n][i];
    int lst=-1;
    for(register int i=1;i<=n;++i)
    {
        for(register int j=1;j<=m;++j)
            f[0][j]=0;
        f[0][0]=1;
        int maxn=1ll<<60;
        bool tag=1;
        for(register int j=1;j<=m;++j)
        {
            int x=a[j]-r[i][j]+l[i][j];
            if(x<=0)
            {
                tag=0;
                break;
            }
            if(b[j]>0)
                maxn=min(maxn,x/b[j]);
            for(register int k=0;k<=m;++k)
            {
                f[j][k]=(f[j-1][k]*x%mod+(k? f[j-1][k-1]*((mod-b[j])%mod)%mod:0))%mod;
                //cout<<f[j][k]<<" ";
            }
            //cout<<endl;
        }
        if(!tag)
            break;
        if(lst^maxn)
        {
            lst=maxn;
            for(register int j=0;j<=m;++j)
            {
                g[j]=solve(maxn,j);
                //cout<<g[j]<<endl;
            }
        }
        //puts("qwq");
        for(register int j=0;j<=m;++j)
            ans=(ans+g[j]*f[m][j]%mod)%mod;
        ans=(ans+f[m][0])%mod;
    }
    printf("%lld\n",(ans+mod)%mod);
    return 0;
}
posted @ 2021-09-28 21:32  绝顶我为峰  阅读(69)  评论(0编辑  收藏  举报