loj3389. 「NOIP2020」微信步數
阿新 • • 發佈:2021-09-28
首先判掉無窮大。
考慮讓人分身到每一個點,然後分別統計每走一步活下來的人數。
不難發現除了第一輪,以後每一輪在某個維度上掛掉的人都是一樣多的,因為每輪過後只有新增加的變化量的部分會掛掉。
於是先把第一輪單獨處理了,後面每一輪其實都是固定的。
快速維護這個貢獻。我們記第 \(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\)
然後裡面是個 \(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; }