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