洛谷 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\) 可以列出以下四个不等式:

\[\begin{cases} 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 队怎么就没了呢

posted @ 2021-08-01 14:26  tzc_wk  阅读(96)  评论(0)    收藏  举报