LGP7116 [NOIP 2020] 微信步数 学习笔记
LGP7116 [NOIP 2020] 微信步数 学习笔记
前言
本文参考OMG_WC的题解。
相比两道构造和三道大 \(\texttt{DS}\),这题也没那么令人生畏。\(\texttt{0904}\) 的我可以说现在只剩五道题补不动了。
题意简述
小 \(\texttt{C}\) 喜欢跑步,并且非常喜欢刷微信步数,为此,他制定了一个计划。
他来到了一个 \(m\) 维的场地。这是一个离散的空间,第 \(i\) 维的长度为 \(w_i\),于是其中的每个点都可以由 \(m\) 维整数坐标 \((a_1,a_2,\dots a_k)\) 表示(当然 \(a_i\in[1,w_i]\),\(i\le [1,m]\))。
小 \(\texttt{C}\) 制定了一个路线,其可以用 \(n\) 个数对 \((c_i,d_i)\) 表示,第 \(i\) 步意为在第 \(c_i\) 维上向某个方向动一步(也即 \(d_i\le \{1,-1\}\))。
每天,他会从一个起点出发,依次执行每一步,第 \(n\) 步执行完了后就再从第 \(1\) 步开始执行。只有当他跑到场地外的时候他才会停止当天的行动。
在他 \(\prod_{i=1}^m w_i\) 天的时间里,他会把空间内每个点都当作一天的起点行动。问他最后总共刷出了多少微信步数。答案对 \(10^9+7\) 取模。
特别地,如果他的计划死循环了,直接输出 \(-1\)。
\(n\le 5\times 10^5\),\(m\le 10\),\(w_i\le 10^9\)。
做法解析
\(\prod_{i=1}^m w_i\) 太大了,所以我们不可能一个一个考虑所有起点,我们要批量地考虑它们。假设我们让所有起点一起走路,我们每走一步就查看当前还有多少个起点存活(也即没出界),把答案加上这个数字,最后就得到答案了。
每一维的出界与否是相互独立的。对于每一维,没出界的坐标显然是一段区间,我们对每一维 \(i\) 记录其最大的往左偏移量 \(l_i\)(注意这个值是负的)和往右偏移量 \(r_i\),显然对于每一步结束后,这一维上最左的 \(-l_i\) 个和最右的 \(r_i\) 个就是不在界内的,而有 \(w_i-r_i-(-l_i)\) 个坐标存活,因此共有 \(\prod_{i=1}^k w_i-r_i+l_i\) 个坐标存活。
我们终止循环走路的判断标准就是有一维没有任何坐标存活。至于 \(-1\) 的情况很好判断:一轮后回到原地且存在某个起点走完一轮出不去就说明会死循环。
那么我们就已经有一个 \(O(nm\max(w_i))\) 的算法了。它可以拿到 \(45\text{pts}\)。
void solve(){
while(1){
for(int i=1;i<=N;i++){
E[C[i]]+=D[i];
minner(L[C[i]],E[C[i]]);
maxxer(R[C[i]],E[C[i]]);
mint res=1;
for(int j=1;j<=M;j++){
if(W[j]-R[j]+L[j]<=0)return;
res*=(W[j]-R[j]+L[j]);
}
ans+=res;
}
int flg=1;
for(int j=1;j<=M;j++)if(E[j]!=0)flg=0;
if(flg){flag=1;return;}
}
}
考虑优化。你很容易发现以上代码计算的严重冗余:对于每一维来讲,第一轮(第一次走完 \(n\) 步)之后的轮次,其新增的出界坐标量是一致的。
比如说对于某一维,第一轮走完后我们得到了 \(l_i,r_i\),还知道了最后的总偏移量 \(e_j\),第二轮的时候显然就是在 \(e_j\) 的方向会多 \(|e_j|\) 个坐标出界。

所以,我们来尝试把答案写成一个总的式子。首先第一轮单独拎出来算,从第二轮开始考虑。
对于第 \(j\) 维,设第一轮后还有 \(a_j\) 个坐标活着,之后每一轮会新增 \(b_j\) 个坐标出界,具体到一轮内(也即考虑最后可能不完整的一轮)前 \(i\) 步后有 \(f_{j,i}\) 个结点出界。
那么就有:对于第 \(2+x\) 轮的第 \(i\) 步,总共新增的对答案贡献就是 \(\prod_{j=1}^m a_j-x\times b_j-f_{j,i}\)。所以,我们最后要算的东西就可以写成 \(\sum_{i=1}^n\sum_{x=0}^T\prod_{j=1}^m a_j-x\times b_j-f_{j,i}\)。
那么这东西咋算呢?如果还是一层层枚举的话,复杂度并不能得到改善。因此,我们选择把这个式子“拆掉”。最外层仍然枚举 \(i\)。把内层的 \(\prod_{j=1}^m a_j-x\times b_j-f_{j,i}\) 写开,可以写成 \(\sum_{k=0}^m g_kx^k\),也就是一个 \(k\) 次多项式 \(G\)。显然,\(x\) 在这是自变量,所以枚举 \(x\) 从 \(0\) 到 \(T\) 的时候,\(g_k\) 都是一样的。所以原式可以进而写为 \(\sum_{i=1}^n\sum_{x=0}^T G(x)=\sum_{i=1}^n\sum_{k=0}^m g_k\times \sum_{x=0}^T x^k\)。这样,你可以先 \(O(m^2)\) 暴力算出所有 \(g_k\);对于 \(\sum_{x=0}^T x^k\),这东西的快速计算请见CFP622F学习笔记。
时间复杂度 \(O(nm^2)\),可以通过。
代码实现
有很多细节。此处暂不展开,可以详见注释版代码。
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
using namespace omodint;
using mint=m107;
const int MaxN=5e5+5,MaxM=20,Inf=1e9;
int N,M,W[MaxM],C[MaxN],D[MaxN],E[MaxM];
int L[MaxM][MaxN],R[MaxM][MaxN],F[MaxM][MaxN];
using namespace omathe;
mint P[MaxN],tprd[MaxM];
mint asolve(int n,int k){
for(int i=1;i<=k+1;i++)P[i]=P[i-1]+fpow(i,k);
return laiitp(P,k+1,n);
}
mint G[MaxM],gt[MaxM],ans;
int A[MaxN],B[MaxN],T,flg;
int main(){
readis(N,M);premwork(M+1);
for(int j=1;j<=M;j++)readi(W[j]);
for(int i=1;i<=N;i++)readis(C[i],D[i]);
for(int i=1,c;i<=N;i++){
c=C[i],E[c]+=D[i];mint tmp=1;
for(int j=1;j<=M;j++)L[j][i]=L[j][i-1],R[j][i]=R[j][i-1];
minner(L[c][i],E[c]),maxxer(R[c][i],E[c]);
for(int j=1,t;j<=M;j++){
t=W[j]-R[j][i]+L[j][i];
tmp*=t;if(t<=0)flg=1;
}
if(flg)break;
ans+=tmp;
}
mint wprd=1;
for(int j=1;j<=M;j++)wprd*=W[j];
ans+=wprd;
if(flg){writi(miti(ans));return 0;};
for(int j=1;j<=M;j++)A[j]=W[j]-R[j][N]+L[j][N],flg|=(E[j]!=0);
if(!flg){puts("-1");return 0;}
for(int j=1;j<=M;j++)B[j]=abs(E[j]);
for(int j=1;j<=M;j++)L[j][0]=L[j][N],R[j][0]=R[j][N];
for(int i=1,c;i<=N;i++){
c=C[i],E[c]+=D[i];
for(int j=1;j<=M;j++)L[j][i]=L[j][i-1],R[j][i]=R[j][i-1],F[j][i]=F[j][i-1];
F[c][i]+=(E[c]<L[c][i]),F[c][i]+=(E[c]>R[c][i]);
minner(L[c][i],E[c]),maxxer(R[c][i],E[c]);
}
T=Inf,flg=0;
for(int i=1;i<=N;i++){
int ct=Inf;
G[0]=1;for(int j=1;j<=M;j++)G[j]=0;
for(int j=1;j<=M;j++){
for(int k=0;k<j;k++)gt[k]=G[k];
int x=A[j]-F[j][i];
if(x<=0){flg=1;break;}
if(B[j])minner(ct,x/B[j]);
for(int k=0;k<j;k++)G[k]*=x;
for(int k=0;k<j;k++)G[k+1]-=gt[k]*B[j];
}
if(flg)break;
if(ct<T){T=ct;tprd[0]=T+1;for(int j=1;j<=M;j++)tprd[j]=asolve(T,j);}
for(int j=0;j<=M;j++)ans+=G[j]*tprd[j];
}
writil(miti(ans));
return 0;
}
浙公网安备 33010602011771号