正题
题目链接:https://www.luogu.com.cn/problem/P7116
题目大意
有一个kkk维空间,第iii维长度为wiw_iwi,有nnn步每一步都是让某个维的坐标+1/−1+1/-1+1/−1,每次走完nnn步都会从111重新走一次,现在求从这个空间的每个点出发走多少步才能走出这个空间的步数的和。
1≤n≤5×1051\leq n\leq 5\times 10^51≤n≤5×105
1≤k≤10,1≤wi≤1061\leq k\leq 10,1\leq w_i\leq 10^61≤k≤10,1≤wi≤106 或者 1≤k≤3,1≤wi≤1091\leq k\leq 3,1\leq w_i\leq 10^91≤k≤3,1≤wi≤109
解题思路
求在这一步出去的方案显然很麻烦,所以我们可以考虑对于每步之后求还没出去的起点数然后求个和就一样了。并且每个维度可以在一定程度上分开考虑。
第一轮显然需要特别处理,设li,j,ri,jl_{i,j},r_{i,j}li,j,ri,j表示第iii步之后第jjj维的最小位移(非正数)和最大位移,那么这一步不会出去的方案就是∏j=1k(wj−ri,j+li,j)\prod_{j=1}^k (w_j-r_{i,j}+l_{i,j})∏j=1k(wj−ri,j+li,j),也就是在[1−l,w−r][1-l,w-r][1−l,w−r]这个范围可以存活,这个可以暴力处理。
之后考虑每轮的第iii步的最小/最大位移距离依旧记作li,j,ri,jl_{i,j},r_{i,j}li,j,ri,j,之后考虑如何计算每一步多少轮之后会死亡,记作ttt。
首先设aia_iai表示第一轮维度iii缩小的范围,然后bib_ibi表示之后每一轮这个维度缩小的范围,那么对于第iii步来说,第jjj个维度的最久存活轮数就是⌊aj−ri,j+li,jbj⌋\lfloor\frac{a_j-r_{i,j}+l_{i,j}}{b_j}\rfloor⌊bjaj−ri,j+li,j⌋,算出ttt之后假设如果我们能够枚举轮数的话那么答案应该就是
∑x=1t∏j=1kaj−ri,j+li,j−bjx\sum_{x=1}^t\prod_{j=1}^ka_j-r_{i,j}+l_{i,j}-b_jxx=1∑tj=1∏kaj−ri,j+li,j−bjx
显然可以O(k2)O(k^2)O(k2)暴力乘算得到一个和xxx有关的多项式然后求和。
至于多项式求和我们可以通过∑x=0txj\sum_{x=0}^t x^j∑x=0txj带入多项式暴算,可以直接拉插得到,当然这题可以对于k≤3k\leq 3k≤3的情况我们自己手动插多项式算,然后k>3k>3k>3的就直接预处理就好了。
时间复杂度:O(nk2)O(nk^2)O(nk2) 或者 O(nk2+k×max{wi})O(nk^2+k\times max\{w_i\})O(nk2+k×max{wi})
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e5+10,M=11,P=1e9+7;
ll n,m,ans,pw[M][N*2],l[N][M],r[N][M],w[M],a[M],b[M],e[M],f[M][M];
ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%P;x=x*x%P;b>>=1;}return ans;
}
ll calc(ll n,ll k){if(k>3)return pw[k][n];if(k==3)return (n*(n+1)/2%P)*((n*(n+1)/2)%P)%P;if(k==2)return n*(n+1)%P*(2*n+1)%P*((P+1)/6)%P;if(k==1)return n*(n+1)/2%P; return n+1;
}
signed main()
{scanf("%lld%lld",&n,&m);for(ll k=4;k<=m;k++)for(ll i=1;i<=1e6;i++)pw[k][i]=(pw[k][i-1]+power(i,k))%P;ans=1;for(ll i=1;i<=m;i++)scanf("%lld",&w[i]),ans=ans*w[i]%P;for(ll i=1;i<=n;i++){ll c,d;scanf("%lld%lld",&c,&d);e[c]+=d;for(ll j=1;j<=m;j++)l[i][j]=l[i-1][j],r[i][j]=r[i-1][j];l[i][c]=min(l[i][c],e[c]);r[i][c]=max(r[i][c],e[c]);}bool flag=1;for(ll i=1;i<=m;i++)if(e[i]!=0||w[i]-r[n][i]+l[n][i]<=0){flag=0;break;}if(flag)return puts("-1")&0;for(ll i=1;i<=n;i++){ll sum=1;for(ll j=1;j<=m;j++)sum=sum*max(0ll,w[j]-r[i][j]+l[i][j])%P;ans=(ans+sum)%P;}for(ll i=1;i<=m;i++)a[i]=w[i]-r[n][i]+l[n][i];for(ll i=1;i<=n;i++)for(ll j=1;j<=m;j++){l[i][j]=min(0ll,l[i][j]+e[j]-l[n][j]);r[i][j]=max(0ll,r[i][j]+e[j]-r[n][j]);}for(ll i=1;i<=m;i++)b[i]=r[n][i]-l[n][i];flag=0;for(ll i=1;i<=n;i++){for(ll j=1;j<=m;j++)f[0][j]=0;f[0][0]=1;ll t=1e9+7;for(ll j=1;j<=m;j++){ll x=a[j]-r[i][j]+l[i][j];if(x<=0){flag=1;break;}if(b[j]>0)t=min(t,x/b[j]);for(ll k=0;k<=m;k++){(f[j][k]=f[j-1][k]*x%P)%=P;if(k)(f[j][k]+=f[j-1][k-1]*(P-b[j])%P)%=P;}}if(flag)break;for(ll j=0;j<=m;j++)(ans=ans+f[m][j]*calc(t,j)%P)%=P;}printf("%lld\n",ans);return 0;
}