[luogu7116]微信步数

先判定无解,当且仅当存在一个位置使得移动$n$步后没有结束且仍在原地

暴力枚举移动的步数,记$S_{i}$为移动$i$步(后)未离开范围的点个数,则恰好移动$i$步的人数为$S_{i-1}-S_{i}$(特别的$S_{0}=P$),答案即为$\sum_{i=1}^{D}(S_{i-1}-S_{i})i=\sum_{i=0}^{D-1}S_{i}$(其中$D=\min_{S_{i}=0}i$)

考虑如何求出$S_{i}$,记$len_{j}$为第$j$维的合法范围,由于每一维互不干扰,则有$S_{i}=\prod_{j=1}^{m}len_{j}$

由于每$n$步必然重复向一个方向移动,那么$D\le n\max w_{i}$,总复杂度即$o(nm\max w_{i})$

进一步优化,将$S_{i}$按$i\ mod\ n$的值分组,对于第$i$组($i\in [0,n)$),$i$单独计算,然后求出$i+n$时的$len_{j}$,记$n$步的总位移为$d_{i}$,则第$kn+i(k\ge 1)$步时$len'_{j}=\max(len_{j}-(k-1)|d_{j}|,0)$

写成式子,即$\sum_{k=1}^{D'}\prod_{j=1}^{m}(len_{j}+|d_{j}|-k|d_{j}|)$(其中$D'=\min_{j=1}^{m}\frac{len_{j}}{|d_{j}|}$,因为$\max$取0后一定为0,不需要再计算)

将后式$o(m^{2})$暴力展开,那么就是一个关于$k$的一个$m+1$次多项式,多项式的前缀和也可以$o(m^{2})$计算,总复杂度即为$o(nm^{2})$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 500005
 4 #define M 20
 5 #define mod 1000000007
 6 int n,m,ans,w[M],x[N],y[N],d[M],mx[N<<1][M],mn[N<<1][M],g[M],sum[M][M];
 7 int ksm(int n,int m){
 8     if (!m)return 1;
 9     int s=ksm(n,m>>1);
10     s=1LL*s*s%mod;
11     if (m&1)s=1LL*s*n%mod;
12     return s;
13 }
14 int mul(int x,int y){
15     for(int i=m+1;i;i--)g[i]=(1LL*g[i-1]*x+1LL*g[i]*y)%mod;
16     g[0]=1LL*g[0]*y%mod;
17 }
18 int calc(int k,int x){
19     int s=1,ans=0;
20     for(int i=0;i<=k+1;i++){
21         ans=(ans+1LL*s*sum[k][i])%mod;
22         s=1LL*s*x%mod;
23     }
24     return ans;
25 }
26 int main(){
27     scanf("%d%d",&n,&m);
28     sum[0][1]=1;
29     for(int i=1;i<=m;i++){//i+1次多项式, 
30         int y=0;
31         for(int x=0;x<=i+1;x++){
32             y=(y+ksm(x,i))%mod;
33             memset(g,0,sizeof(g));
34             g[0]=y;
35             for(int j=0;j<=i+1;j++)
36                 if (j!=x){
37                     mul(1,mod-j);
38                     mul(0,ksm((x-j+mod)%mod,mod-2));
39                 }
40             for(int j=0;j<=i+1;j++)sum[i][j]=(sum[i][j]+g[j])%mod;
41         }
42     }
43     for(int i=1;i<=m;i++)scanf("%d",&w[i]);
44     for(int i=1;i<=n;i++){
45         scanf("%d%d",&x[i],&y[i]);
46         d[x[i]]+=y[i];
47         for(int j=1;j<=m;j++){
48             mx[i][j]=max(mx[i-1][j],d[j]);
49             mn[i][j]=min(mn[i-1][j],d[j]);
50         }
51     }
52     for(int i=n+1;i<=2*n;i++){
53         d[x[i-n]]+=y[i-n];
54         for(int j=1;j<=m;j++){
55             mx[i][j]=max(mx[i-1][j],d[j]);
56             mn[i][j]=min(mn[i-1][j],d[j]);
57         }
58     }
59     for(int i=1;i<=m;i++)d[i]/=2;
60     bool flag1=0,flag2=0;
61     for(int i=1;i<=m;i++)
62         if (d[i])flag1=1;
63     for(int i=1;i<=m;i++)
64         if (mx[n][i]-mn[n][i]>w[i])flag2=1;
65     //flag1说明离开了原位置,flag2说明一定是一轮 
66     if ((!flag1)&&(!flag2)){
67         printf("-1");
68         return 0;
69     }
70     for(int i=0;i<n;i++){
71         int s=1;
72         for(int j=1;j<=m;j++)s=1LL*s*max(w[j]-(mx[i][j]-mn[i][j]),0)%mod;
73         ans=(ans+s)%mod;
74     }
75     for(int i=n;i<2*n;i++){
76         bool flag=0;
77         for(int j=1;j<=m;j++)
78             if (w[j]-(mx[i][j]-mn[i][j])<0)flag=1;
79         if (flag)break;
80         memset(g,0,sizeof(g));
81         g[0]=1;
82         for(int j=1;j<=m;j++)mul(mod-abs(d[j]),w[j]-(mx[i][j]-mn[i][j])+abs(d[j]));
83         int D=0x3f3f3f3f;
84         for(int j=1;j<=m;j++)
85             if (d[j])D=min(D,(w[j]-(mx[i][j]-mn[i][j]))/abs(d[j])+1);
86         for(int j=0;j<=m;j++)ans=(ans+1LL*g[j]*calc(j,D))%mod;
87     }
88     printf("%d",ans);
89 } 
View Code

 

posted @ 2020-12-16 22:06  PYWBKTDA  阅读(111)  评论(0编辑  收藏  举报