洛谷 P7116 - [NOIP2020] 微信步数(拉格朗日插值)
我竟然独立切掉了这道题!incredible!
纪念我逝去的一上午(NOIP 总时长 4.5h,这题做了我整整 4.5h)
首先讲一下现场我想的 80 分的做法,虽然最后挂成了 65 分,但大概率是被卡常了(
注意到虽然点数高达 \(\prod\limits_{i=1}^kw_i\),但每一维我们都可以单独考虑,具体来说,我们设 \(tim_{i,j}\) 表示只考虑 \(c_k=i\) 的 \(k\),当前第 \(i\) 维坐标是 \(j\),最少需要多少步才能离开场地,\(tim_{i,j}\) 显然 xjb 分类讨论就能求出。那么由于每一维的独立性,对于一个坐标 \((x_1,x_2,\cdots,x_k)\),它所需要的离开场地的最小时间就是对应每一维所需时间取 \(\min\),即 \(\min\limits_{i=1}^ktim_{i,x_i}\),也就是说我们要求的东西就是 \(\sum\limits_{x_i\le w_i}\min\limits_{i=1}^ktim_{i,x_i}\),我们考虑将 \(tim_{i,j}\) 全部揉在一起从大到小排序并枚举 \(\min\limits_{i=1}^ktim_{i,x_i}=tim_{x,y}\),那么此时合法的坐标数目就是 \(\prod\limits_{j\ne x}cnt_j\),其中 \(cnt_j=\sum\limits_{k=1}^{w_j}[tim_{j,k}>tim_{x,y}]\),这个值可以通过预处理逆元在枚举的过程中 \(\mathcal O(1)\) 求得。那么,加上排序的复杂度,总复杂度大概是 \(nk\log nk\)。然鹅这个复杂度不开 O2 是过不去的,有我现场写的程序为证。因此考虑加点小小的剪枝,注意到 \(tim_{i,j}\) 随着 \(j\) 的增大是一个凸函数,因此咱考虑找到该函数的峰点,然后将两部分归并起来即可在 \(\mathcal O(w_i)\) 的时间内将某个 \(tim_{i}\) 从大到小排序,然后再将所有 \(tim_i\) 再归并起来即可。然鹅有一个让人非常哭笑不得的细节,就是在对全部 \(k\) 个数组归并时,找最小值用 priority_queue
,时间复杂度 \(\mathcal O(nk\log k)\) 反而 TLE,改成暴力 \(\mathcal O(k)\) 找反而就过了,大概是常数的问题吧。
附:80 分的代码:
const int MAXN=5e5;
const int MAXK=10;
const int MOD=1e9+7;
int n,k,c[MAXN+5],d[MAXN+5],w[MAXK+2];
namespace sub1{
bool check(){
int flg=1;
for(int i=1;i<=k;i++) flg&=(w[i]<=1e6);
return flg;
}
const int MAXW=1e6;
const ll INF=0x3f3f3f3f3f3f3f3fll;
ll tim[MAXK+2][MAXW+5];
int lft[MAXW+5],rit[MAXW+5];
void calc(int x){
memset(lft,0,sizeof(lft));memset(rit,0,sizeof(rit));
int mn=0,mx=0,cur=0;
for(int i=1;i<=n;i++) if(c[i]==x) cur+=d[i];
if(cur<0){
for(int i=1;i<=n;i++) if(c[i]==x) d[i]=-d[i];
} cur=0;
for(int i=1;i<=n;i++) if(c[i]==x){
cur+=d[i];
if(cur>mx) rit[cur]=i,mx=cur;
if(cur<mn) lft[-cur]=i,mn=cur;
}
for(int i=1;i<=w[x];i++){
tim[x][i]=INF;
if(i+mn<=0) chkmin(tim[x][i],(ll)lft[i]);
if(i+mx>w[x]) chkmin(tim[x][i],(ll)rit[w[x]+1-i]);
if(cur){
int bound=w[x]-mx+1;
int tt=max((bound-i+cur-1)/cur,0);
chkmin(tim[x][i],1ll*tt*n+rit[w[x]+1-(i+tt*cur)]);
}
}
} ll A[MAXW+5],B[MAXW+5];
int inv[MAXW+5];
void mergesort(int x){
int ps=0,c1=0,c2=0;
for(int i=1;i<=w[x];i++) if(tim[x][i]>tim[x][i+1]){ps=i;break;}
for(int i=ps;~i;i--) A[++c1]=tim[x][i];A[c1+1]=0;
for(int i=ps+1;i<=w[x];i++) B[++c2]=tim[x][i];B[c2+1]=0;
for(int i=1,p1=1,p2=1;i<=w[x];i++){
if(A[p1]>B[p2]) tim[x][i]=A[p1++];
else tim[x][i]=B[p2++];
}
}
int cnt[MAXK+5];
void solve(){
for(int i=1;i<=k;i++) calc(i);
for(int i=1;i<=k;i++) mergesort(i);
for(int i=(inv[0]=inv[1]=1)+1;i<=MAXW;i++)
inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
int sm=0;for(int i=1;i<=k;i++) sm+=w[i];
bool flg=1;
for(int i=1;i<=k;i++) flg&=(tim[i][1]==INF);
if(flg) return puts("-1"),void();
int res=1,cnt0=k,ans=0;
for(int i=1;i<=sm;i++){
int x=0;
for(int j=1;j<=k;j++) if(tim[j][cnt[j]+1]>tim[x][cnt[x]+1]) x=j;
if(!cnt[x]) cnt0--;
else res=1ll*res*inv[cnt[x]]%MOD*(cnt[x]+1)%MOD;
++cnt[x];
if(!cnt0&&tim[x][cnt[x]]) ans=(ans+tim[x][cnt[x]]%MOD*res%MOD*inv[cnt[x]])%MOD;
} printf("%d\n",ans);
}
}
int main(){
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) scanf("%d%d",&c[i],&d[i]);
if(sub1::check()) sub1::solve();
return 0;
}
接下来考虑正解,也就是这个脑子不太好使的人今早灵机一动想出来的做法。
我们考虑预处理出这样几个量:
- \(x_i\):在一整个长度为 \(n\) 周期中,如果我们将起点视为源点,那么在一个周期中第 \(i\) 维坐标的最小值是多少。
- \(y_i\):在一整个长度为 \(n\) 周期中,如果我们将起点视为源点,那么在一个周期中第 \(i\) 维坐标的最大值是多少。
- \(t_i\):在一整个长度为 \(n\) 周期中,如果我们将起点视为源点,那么在一个周期结束后第 \(i\) 维的坐标是多少。方便起见我们假设 \(t_i\ge 0\),如果 \(t_i<0\) 那我们只需将 \(c_j=i\) 的 \(d_j\) 都变为其相反数即可。
- \(mn_{i,j}\):在一整个长度为 \(n\) 周期中,如果我们将起点视为源点,那么执行完前 \(j\) 个命令后,在一个周期中第 \(i\) 维坐标的最小值是多少,不难发现 \(x_i=mn_{i,n}\)。
- \(mx_{i,j}\):在一整个长度为 \(n\) 周期中,如果我们将起点视为源点,那么执行完前 \(j\) 个命令后,在一个周期中第 \(i\) 维坐标的最大值是多少,同样地有 \(y_i=mx_{i,n}\)。
- \(sum_{i,j}\):在一整个长度为 \(n\) 周期中,如果我们将起点视为源点,那么执行完前 \(j\) 个命令后,第 \(i\) 维的坐标是多少。
首先我们思考一下对于某一维 \(i\) 上的某个坐标 \(x\),最少需要多少步才能离开这一维的范围(即,这一维的坐标 \(\notin[1,w_i]\))。一个比较显然但非常关键的性质是,由于 \(t_i\ge 0\),因此如果第一个周期内不能从左边离开场地,那么第二周期起点位置肯定比第一周期更靠右,也就更不可能从右边离开场地,也就是说如果离开时走的步数 \(>n\),那么我们必定是从右边离开场地的。因此我们考虑枚举小 C 最后一步是在这个长度为 \(n\) 的周期中哪个指令下离开场地,记为第 \(i\) 个命令。那么此时此刻已经执行的命令必定是若干个长度为 \(n\) 的完整的周期加上前 \(i\) 个命令构成的这段前缀,我们记当前位于第 \(K\) 个周期(方便起见我们不妨设 \(K\ge 2\),\(K=1\) 的情况后面再说),那么根据之前的推论,这一步肯定是第 \(c_i\) 维坐标达到了 \(w_{c_i}+1\)。又因为第一周期内无法离开场地,因此如果我们记 \(s_i\) 为第 \(i\) 起点的坐标,必定有 \(s_j\in[-x_j+1,w_j]\),又因为在前 \((K-1)n+i-1\) 步内不能离开场地,因此对于每一维 \(j\) 可以列出以下四个不等式:
s_j+(K-2)t_j+mn_{j,i-1}\in[1,w_j]\\
s_j+(K-2)t_j+mx_{j,i-1}\in[1,w_j]\\
s_j+(K-1)t_j+x_j\in[1,w_j]\\
s_j+(K-1)t_j+y_j\in[1,w_j]
\end{cases}
\]
再加上第 \(i\) 步第 \(c_i\) 维坐标需达到 \(w_{c_i}+1\),因此还有 \(s_{c_i}+(K-1)t_{c_i}+sum_{c_i,i-1}=w_{c_i}+1\)。
总共是 \(5k+1\) 个限制,每个限制都可以表示为 \(s_j\in[L,R]\) 的形式,因此我们可以解出 \(s_j\) 的范围,如果存在某个 \(s_j\) 的范围为空则表明不可能恰好在第 \((K-1)n+i\) 步离开场地,否则合法的坐标数就是 \(s_j\) 范围的乘积。
但是直接枚举 \(K\) 复杂度过高,无法接受。不过注意到一个性质就是如果存在某个坐标经过 \(Kn+i\) 步离开场地,就必定存在某个坐标经过 \((K-1)n+i\) 步离开场地,因此考虑二分最大的 \(K\) 满足存在某个坐标经过 \((K-1)n+i\) 步离开场地,那么我们要求的值就是 \(\sum\limits_{j=2}^K\text{calc}((j-1)n+i)·((j-1)n+i)\),其中 \(\text{calc}(x)\) 为满足恰好经过 \(x\) 步离开场地的坐标个数。暴力算还是 T,不过我们还可以发现,每次解出来的 \(s_j(j\ne c_i)\) 的范围区间的左端点都是固定不变的值 \(-x_j+1\),因此区间长度可以写成关于右端点的一次函数,又区间非空,因此右端点可以写成关于 \(K\) 的一次函数,因此 \(\text{calc}((j-1)n+i)\) 可以看作关于 \(j\) 的 \(k-1\) 次函数,再加上前缀和与后面的系数,\(\sum\) 里的东西为关于 \(K\) 的 \(k+1\) 次函数,因此可以考虑求出 \(2,3,\cdots,k+3\) 处的函数值然后把多项式插出来即可。
接下来考虑 \(K=1\) 的情况,相信聪明的读者们如果理解了 \(K\ge 2\) 的情况,那么 \(K=1\) 的情况想必是易如反掌了,我们还是可以列出一些关于 \(s_j\) 的不等式,也就能解出对应 \(s_j\) 的范围,然后可以乘法原理对应的坐标个数,如果还是不懂那就看代码吧。
上述算法复杂度是 \(nk\log n+nk^2\),不开 O2 只能获得 80 分的好成绩。
附:不开 O2 80pts,开 O2 AC 的代码:
namespace sub2{
int x[MAXK+2],y[MAXK+2],t[MAXK+2];
int mn[MAXK+2][MAXN+5],mx[MAXK+2][MAXN+5],sum[MAXK+2][MAXN+5];
void calc(int r){
for(int i=1;i<=n;i++) if(c[i]==r) t[r]+=d[i];
if(t[r]<0){
t[r]=-t[r];
for(int i=1;i<=n;i++) if(c[i]==r) d[i]=-d[i];
} int cur=0;
for(int i=1;i<=n;i++){
if(c[i]==r) cur+=d[i];sum[r][i]=cur;
mn[r][i]=min(mn[r][i-1],cur);
mx[r][i]=max(mx[r][i-1],cur);
} x[r]=mn[r][n];y[r]=mx[r][n];
// printf("%d %d\n",x[r],y[r]);
}
struct itvl{
ll l,r;
itvl(ll _l=0,ll _r=0):l(_l),r(_r){}
itvl operator &(const itvl &rhs){
return itvl(max(l,rhs.l),min(r,rhs.r));
}
};
int calc(int mid,int id){
static itvl it[MAXK+2];
for(int i=1;i<=k;i++) it[i]=itvl(-x[i]+1,w[i]);
for(int i=1;i<=k;i++){
it[i]=it[i]&itvl(1-1ll*(mid-2)*t[i]-y[i],w[i]-1ll*(mid-2)*t[i]-y[i]);
it[i]=it[i]&itvl(1-1ll*(mid-1)*t[i]-mx[i][id-1],w[i]-1ll*(mid-1)*t[i]-mx[i][id-1]);
} it[c[id]]=it[c[id]]&itvl(w[c[id]]+1-1ll*(mid-1)*t[c[id]]-sum[c[id]][id],w[c[id]]+1-1ll*(mid-1)*t[c[id]]-sum[c[id]][id]);
// printf("checking %d %d\n",mid,id);
// for(int i=1;i<=k;i++) printf("%lld %lld\n",it[i].l,it[i].r);
int res=1;
for(int i=1;i<=k;i++){
if(it[i].l>it[i].r) return 0;
res=1ll*res*(it[i].r-it[i].l+1)%MOD;
} return res;
}
int ff[10],inv[10];
void solve(){
for(int i=(inv[0]=inv[1]=1)+1;i<=6;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=k;i++) calc(i);
// for(int i=1;i<=k;i++) printf("%d %d %d\n",x[i],y[i],t[i]);
int res=0;
for(int i=1;i<=n;i++) if(d[i]==1){
int l=2,r=1e9,p=-1;
if(calc(1e9+1,i)) return puts("-1"),void();
while(l<=r){
int mid=l+r>>1;
if(calc(mid,i)) p=mid,l=mid+1;
else r=mid-1;
} //printf("%d\n",p);
if(~p){
for(int j=2;j<=min(p,6);j++){
// printf("%d %d %d\n",j,i,calc(j,i));
ff[j]=1ll*calc(j,i)*(1ll*(j-1)*n%MOD+i)%MOD;
ff[j]=(ff[j-1]+ff[j])%MOD;
} if(p<=6) res=(res+ff[p])%MOD;
else{
for(int j=2;j<=6;j++){
int ss=1;
for(int k=2;k<=6;k++) if(j^k){
int mul=1ll*(p-k+MOD)*inv[abs(j-k)]%MOD;
if(j<k) mul=MOD-mul;ss=1ll*ss*mul%MOD;
} res=(res+1ll*ss*ff[j])%MOD;
}
}
}
}
for(int i=1;i<=n;i++){
static itvl it[MAXK+2];
for(int j=1;j<=k;j++) it[j]=itvl(1,w[j]);
for(int j=1;j<=k;j++){
it[j]=it[j]&itvl(1-mn[j][i-1],w[j]-mn[j][i-1]);
it[j]=it[j]&itvl(1-mx[j][i-1],w[j]-mx[j][i-1]);
} if(!~d[i]){
it[c[i]]=it[c[i]]&itvl(0-sum[c[i]][i],0-sum[c[i]][i]);
} else {
it[c[i]]=it[c[i]]&itvl(w[c[i]]+1-sum[c[i]][i],w[c[i]]+1-sum[c[i]][i]);
} int mul=i;
// printf("%d:\n",i);
// for(int j=1;j<=k;j++) printf("%lld %lld\n",it[j].l,it[j].r);
for(int j=1;j<=k;j++){
if(it[j].l>it[j].r) mul=0;
else mul=1ll*mul*(it[j].r-it[j].l+1)%MOD;
} res=(res+mul)%MOD;
}
printf("%d\n",res);
}
}
int main(){
// freopen("walk4.in","r",stdin);
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) scanf("%d%d",&c[i],&d[i]);
if(sub1::check()) sub1::solve();
else sub2::solve();
return 0;
}
接下来考虑加点优化,显然复杂度瓶颈在于二分,注意到在二分检验的过程中,我们要用到很多不等式,不过这些不等式都可以写成 \(s_j\in[1-Kt_j+B,w_j-Kt_j+B]\) 的形式,又根据之前的推理这些区间的交的左端点恰好是 \(-x_j+1\),因此我们只用考虑右端点的限制,即 \(B\) 最小的限制即可,由于这些区间的交集非空,必然可以得到 \(w_j-Kt_j+B\ge -x_j+1\),移个项可以得到 \(Kt_j\le w_j+B+x_j-1\),解得 \(K\le\lfloor\dfrac{w_j+B+x_j-1}{t_j}\rfloor\),也就是说这些限制归结到底都可以写成 \(K\le X\) 的形式,然后对这些 \(X\) 取个 \(\min\) 即可得到 \(K\) 的最大值,这样就省去了二分的 \(\log\)。
还有就是上面的代码中插值用了暴力 \(k^2\) 的插值方法,不过由于下标连续,可以 \(\mathcal O(k)\) 插值,这样常数会小不少。
最后附上我最后一次提交的完整代码,开了 O2 只需 947ms:
const int MAXN=5e5;
const int MAXK=10;
const int MOD=1e9+7;
int n,k,c[MAXN+5],d[MAXN+5],w[MAXK+2];
namespace sub1{
bool check(){
int flg=1;
for(int i=1;i<=k;i++) flg&=(w[i]<=1e6);
return flg;
}
const int MAXW=1e6;
const ll INF=0x3f3f3f3f3f3f3f3fll;
ll tim[MAXK+2][MAXW+5];
int lft[MAXW+5],rit[MAXW+5];
void calc(int x){
memset(lft,0,sizeof(lft));memset(rit,0,sizeof(rit));
int mn=0,mx=0,cur=0;
for(int i=1;i<=n;i++) if(c[i]==x) cur+=d[i];
if(cur<0){
for(int i=1;i<=n;i++) if(c[i]==x) d[i]=-d[i];
} cur=0;
for(int i=1;i<=n;i++) if(c[i]==x){
cur+=d[i];
if(cur>mx) rit[cur]=i,mx=cur;
if(cur<mn) lft[-cur]=i,mn=cur;
}
for(int i=1;i<=w[x];i++){
tim[x][i]=INF;
if(i+mn<=0) chkmin(tim[x][i],(ll)lft[i]);
if(i+mx>w[x]) chkmin(tim[x][i],(ll)rit[w[x]+1-i]);
if(cur){
int bound=w[x]-mx+1;
int tt=max((bound-i+cur-1)/cur,0);
chkmin(tim[x][i],1ll*tt*n+rit[w[x]+1-(i+tt*cur)]);
}
}
} ll A[MAXW+5],B[MAXW+5];
int inv[MAXW+5];
void mergesort(int x){
int ps=0,c1=0,c2=0;
for(int i=1;i<=w[x];i++) if(tim[x][i]>tim[x][i+1]){ps=i;break;}
for(int i=ps;~i;i--) A[++c1]=tim[x][i];A[c1+1]=0;
for(int i=ps+1;i<=w[x];i++) B[++c2]=tim[x][i];B[c2+1]=0;
for(int i=1,p1=1,p2=1;i<=w[x];i++){
if(A[p1]>B[p2]) tim[x][i]=A[p1++];
else tim[x][i]=B[p2++];
}
}
int cnt[MAXK+5];
void solve(){
for(int i=1;i<=k;i++) calc(i);
for(int i=1;i<=k;i++) mergesort(i);
for(int i=(inv[0]=inv[1]=1)+1;i<=MAXW;i++)
inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
int sm=0;for(int i=1;i<=k;i++) sm+=w[i];
bool flg=1;
for(int i=1;i<=k;i++) flg&=(tim[i][1]==INF);
if(flg) return puts("-1"),void();
int res=1,cnt0=k,ans=0;
for(int i=1;i<=sm;i++){
int x=0;
for(int j=1;j<=k;j++) if(tim[j][cnt[j]+1]>tim[x][cnt[x]+1]) x=j;
if(!cnt[x]) cnt0--;
else res=1ll*res*inv[cnt[x]]%MOD*(cnt[x]+1)%MOD;
++cnt[x];
if(!cnt0&&tim[x][cnt[x]]) ans=(ans+tim[x][cnt[x]]%MOD*res%MOD*inv[cnt[x]])%MOD;
} printf("%d\n",ans);
}
}
namespace sub2{
int x[MAXK+2],y[MAXK+2],t[MAXK+2];
int mn[MAXK+2][MAXN+5],mx[MAXK+2][MAXN+5],sum[MAXK+2][MAXN+5];
void calc(int r){
for(int i=1;i<=n;i++) if(c[i]==r) t[r]+=d[i];
if(t[r]<0){
t[r]=-t[r];
for(int i=1;i<=n;i++) if(c[i]==r) d[i]=-d[i];
} int cur=0;
for(int i=1;i<=n;i++){
if(c[i]==r) cur+=d[i];sum[r][i]=cur;
mn[r][i]=min(mn[r][i-1],cur);
mx[r][i]=max(mx[r][i-1],cur);
} x[r]=mn[r][n];y[r]=mx[r][n];
}
struct itvl{
ll l,r;
itvl(ll _l=0,ll _r=0):l(_l),r(_r){}
itvl operator &(const itvl &rhs){
return itvl(max(l,rhs.l),min(r,rhs.r));
}
};
int calc(int mid,int id){
static itvl it[MAXK+2];
for(int i=1;i<=k;i++) it[i]=itvl(-x[i]+1,w[i]);
for(int i=1;i<=k;i++){
it[i]=it[i]&itvl(1-1ll*(mid-2)*t[i]-y[i],w[i]-1ll*(mid-2)*t[i]-y[i]);
it[i]=it[i]&itvl(1-1ll*(mid-1)*t[i]-mx[i][id-1],w[i]-1ll*(mid-1)*t[i]-mx[i][id-1]);
} it[c[id]]=it[c[id]]&itvl(w[c[id]]+1-1ll*(mid-1)*t[c[id]]-sum[c[id]][id],w[c[id]]+1-1ll*(mid-1)*t[c[id]]-sum[c[id]][id]);
int res=1;
for(int i=1;i<=k;i++){
if(it[i].l>it[i].r) return 0;
res=1ll*res*(it[i].r-it[i].l+1)%MOD;
} return res;
}
int ff[15],ifac[15],pre[15],suf[15];
void solve(){
for(int i=(ifac[0]=ifac[1]=1)+1;i<=k+3;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=k+3;i++) ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
for(int i=1;i<=k;i++) calc(i);
bool flg=1;
for(int i=1;i<=k;i++) flg&=(!t[i]&&y[i]-x[i]+1<=w[i]);
if(flg) return puts("-1"),void();
int res=0;
for(int i=1;i<=n;i++) if(d[i]==1){
if(!calc(2,i)) continue;
int p=1e9+1;
for(int j=1;j<=k;j++){
if(t[j]){
chkmin(p,(w[j]+2*t[j]-y[j]+x[j]-1)/t[j]);
chkmin(p,(w[j]+t[j]-mx[j][i-1]+x[j]-1)/t[j]);
}
}
// while(l<=r){
// int mid=l+r>>1;
// if(calc(mid,i)) p=mid,l=mid+1;
// else r=mid-1;
// }
for(int j=2;j<=min(p,k+3);j++){
ff[j]=1ll*calc(j,i)*(1ll*(j-1)*n%MOD+i)%MOD;
ff[j]=(ff[j-1]+ff[j])%MOD;
} if(p<=k+3) res=(res+ff[p])%MOD;
else{
pre[1]=suf[k+4]=1;
for(int j=2;j<=k+3;j++) pre[j]=1ll*pre[j-1]*(p-j+MOD)%MOD;
for(int j=k+3;j>>1;j--) suf[j]=1ll*suf[j+1]*(p-j+MOD)%MOD;
for(int j=2;j<=k+3;j++){
int mul=1ll*pre[j-1]*suf[j+1]%MOD;
mul=1ll*mul*ifac[j-2]%MOD*ifac[k+3-j]%MOD;
if((k+3-j)&1) mul=MOD-mul;
res=(res+1ll*mul*ff[j])%MOD;
}
}
}
for(int i=1;i<=n;i++){
static itvl it[MAXK+2];
for(int j=1;j<=k;j++) it[j]=itvl(1,w[j]);
for(int j=1;j<=k;j++){
it[j]=it[j]&itvl(1-mn[j][i-1],w[j]-mn[j][i-1]);
it[j]=it[j]&itvl(1-mx[j][i-1],w[j]-mx[j][i-1]);
} if(!~d[i]) it[c[i]]=it[c[i]]&itvl(0-sum[c[i]][i],0-sum[c[i]][i]);
else it[c[i]]=it[c[i]]&itvl(w[c[i]]+1-sum[c[i]][i],w[c[i]]+1-sum[c[i]][i]);
int mul=i;
for(int j=1;j<=k;j++){
if(it[j].l>it[j].r) mul=0;
else mul=1ll*mul*(it[j].r-it[j].l+1)%MOD;
} res=(res+mul)%MOD;
}
printf("%d\n",res);
}
}
int main(){
// freopen("walk4.in","r",stdin);
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++) scanf("%d%d",&c[i],&d[i]);
if(sub1::check()) sub1::solve();
else sub2::solve();
return 0;
}
啊啊啊啊啊啊啊我的 E 队怎么就没了呢
洛谷 P7116 - [NOIP2020] 微信步数(拉格朗日插值)的更多相关文章
- [洛谷P4781]【模板】拉格朗日插值
题目大意:给你$n(n\leqslant2000)$个点,要你求$n-1$次经过这$n$个点的多项式在$k$处的值 题解:$Lagrange$插值:$$f_x=\sum\limits_{i=1}^ky ...
- 洛谷P4781 【模板】拉格朗日插值(拉格朗日插值)
题意 题目链接 Sol 记得NJU有个特别强的ACM队叫拉格朗,总感觉少了什么.. 不说了直接扔公式 \[f(x) = \sum_{i = 1}^n y_i \prod_{j \not = i} \f ...
- 【洛谷5437】【XR-2】约定(拉格朗日插值)
[洛谷5437][XR-2]约定(拉格朗日插值) 题面 洛谷 题解 首先发现每条边除了边权之外都是等价的,所以可以考虑每一条边的出现次数. 显然钦定一条边之后构成生成树的方案数是\(2*n^{n-3} ...
- 洛谷 P5469 - [NOI2019] 机器人(区间 dp+拉格朗日插值)
洛谷题面传送门 神仙题,放在 D1T2 可能略难了一点( 首先显然对于 P 型机器人而言,将它放在 \(i\) 之后它会走到左边第一个严格 \(>a_i\) 的位置,对于 Q 型机器人而言,将它 ...
- 洛谷P5437/5442 约定(概率期望,拉格朗日插值,自然数幂)
题目大意:$n$ 个点的完全图,点 $i$ 和点 $j$ 的边权为 $(i+j)^k$.随机一个生成树,问这个生成树边权和的期望对 $998244353$ 取模的值. 对于P5437:$1\le n\ ...
- 洛谷 P3270 - [JLOI2016]成绩比较(容斥原理+组合数学+拉格朗日插值)
题面传送门 考虑容斥.我们记 \(a_i\) 为钦定 \(i\) 个人被 B 神碾压的方案数,如果我们已经求出了 \(a_i\) 那么一遍二项式反演即可求出答案,即 \(ans=\sum\limits ...
- LOJ 2743(洛谷 4365) 「九省联考 2018」秘密袭击——整体DP+插值思想
题目:https://loj.ac/problem/2473 https://www.luogu.org/problemnew/show/P4365 参考:https://blog.csdn.net/ ...
- 洛谷P5158 【模板】多项式快速插值
题面 传送门 前置芝士 拉格朗日插值,多项式多点求值 题解 首先根据拉格朗日插值公式我们可以暴力\(O(n^2)\)插出这个多项式,然而这显然是\(gg\)的 那么看看怎么优化,先来看一看拉格朗日插值 ...
- 【Luogu4781】【模板】拉格朗日插值
[Luogu4781][模板]拉格朗日插值 题面 洛谷 题解 套个公式就好 #include<cstdio> #define ll long long #define MOD 998244 ...
随机推荐
- 21.6.4 test
\(NOI\) 模拟赛 太离谱了,碳基生物心态极限 \(T1\),字符串滚出OI,最后想了个区间dp,期望得分32pts,实际得分0pts,不知为啥挂了.正解是没学过的SAM. \(T2\),正解博弈 ...
- 痞子衡嵌入式:超级下载算法RT-UFL v1.0在Segger Ozone下的使用
痞子衡主导的"学术"项目 <RT-UFL - 一个适用全平台i.MXRT的超级下载算法设计> v1.0 版发布近 4 个月了,部分客户已经在实际项目开发调试中用上了这个 ...
- 链表中倒数第K个结点 牛客网 程序员面试金典 C++ Python
链表中倒数第K个结点 牛客网 程序员面试金典 C++ Python 题目描述 输入一个链表,输出该链表中倒数第k个结点. C++ /* struct ListNode { int val; struc ...
- Spring Cloud Alibaba 使用RestTemplate进行服务消费
创建服务提供者工程 创建spring-cloud-alibaba-service-member工程,会员中心服务该服务提供用户会员信息. pom.xml <?xml version=" ...
- elasticsearch7.x配置文件
前言: 以下配置文件基于elasticsearch-7.13.4版本,当然也适用于其它7.x版本 集群环境: 部署3个节点的集群,各个节点不做角色区分,既是master,也是data,在性能 上这种方 ...
- Maven下载、安装、配置
简介 Maven是一个项目管理工具,主要用于Java平台的项目构建.依赖管理和项目生命周期管理. 当然对于我这样的程序猿来说,最大的好处就是对jar包的管理比较方便,只需要告诉Maven需要哪些jar ...
- gcc: fatal error: limits.h: No such file or directory on macos
重装gcc brew install gcc 软链接链到新的gcc和g++ https://stackoverflow.com/questions/56280122/gcc-fatal-error-l ...
- SpringBoot 整合 Mybatis-Plus + Mysql
mybatis-plus是mybatis的一款插件,它的主要作用是快速开发,省略mybatis的配置,具体的功能请参照官网. 开发环境: springboot,maven,mybatis-plus,m ...
- 开发中常见的@NotNull,@NotBlank,@NotEmpty注解的区别
开发中常看见@NotNull,@NotBlank,@NotEmpty三个注解,但却没有深入了解过,下面介绍一下他们的应用场景和区别 @NotNull:主要用在基本数据类型上(Int,Integer,D ...
- k8s网络模型与集群通信
在k8s中,我们的应用会以pod的形式被调度到各个node节点上,在设计集群如何处理容器之间的网络时是一个不小的挑战,今天我们会从pod(应用)通信来展开关于k8s网络的讨论. 小作文包含如下内容: ...