1. 程式人生 > 其它 >loj3389. 「NOIP2020」微信步數

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;
}