Codeforces 1495E - Qingshan and Daniel

发布时间 2023-07-14 14:35:30作者: tzc_wk

假设 \(1\) 号队伍机器人总牌数比 \(2\) 号队伍多,那么显然最终 \(2\) 号队伍中的牌都会走光。

如果 \(1\) 号机器人属于 \(1\) 号队伍那么我们暴力模拟第一轮即可。下面只讨论 \(1\) 号机器人属于 \(2\) 号队伍的情况。

由于我们走牌顺序一定是 \(212121212\cdots 21\) 直到没有 \(2\) 可走为止,因此一个 \(2\) 出牌必然能导致某个 \(1\) 跟了下一张牌,但是比较棘手的地方在于我们是按时间线增大的顺序模拟这个过程,这样要快速找到下一个出牌的机器人最低只能做到 \(O(nV)\)。但是非常关键的一个地方在于,你发现时间线其实是没有用的,也就是说,你以任意顺序执行“挑选一个 \(2\),匹配它下一个 \(1\)”的操作,得到的每个 \(1\) 被匹配的次数都是相同的。

这样就好办了,我们改成以位置增大的顺序模拟这个过程,实时记录下前面有多少个 \(2\) 还没被匹配即可,类似于括号匹配,由于是个环所以要断环成链做两次。时间复杂度线性。

const int MAXN=5e6;
const int MAXM=2e5;
const int MOD=1e9+7;
int n,m,a[MAXN+5],tmp[MAXN+5],t[MAXN+5];ll sum[2];
int p[MAXM+5],k[MAXM+5],b[MAXM+5],w[MAXM+5],base,seed;
int rng(){int ret=seed;seed=(1ll*seed*base+233)%MOD;return ret;}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)scanf("%d%d%d%d",&p[i],&k[i],&b[i],&w[i]);
	for(int i=1;i<=m;i++){
		seed=b[i];base=w[i];
		for(int j=p[i-1]+1;j<=p[i];j++){
			t[j]=rng()%2;a[j]=rng()%k[i]+1;
			sum[t[j]]+=a[j];tmp[j]=a[j];
		}
	}
//	for(int i=1;i<=n;i++)printf("%d%c",a[i]," \n"[i==n]);
//	for(int i=1;i<=n;i++)printf("%d%c",t[i]," \n"[i==n]);
	int fst=1;
	if(sum[t[1]]>sum[t[1]^1]){
		a[1]--;sum[t[1]]--;
		while(fst<=n&&t[fst]==t[1])++fst;
	}
	if(fst==n+1){
		int res=1;
		for(int i=1;i<=n;i++)res=1ll*(((tmp[i]-a[i])^(1ll*i*i))+1)%MOD*res%MOD;
		printf("%d\n",res);return 0;
	}
	int cur=fst;ll cnt=0;
	for(int _=1;_<=n*2;_++){
		if(t[cur]==t[fst])cnt+=a[cur],a[cur]=0;
		else{
			ll can=min((ll)a[cur],cnt);
			cnt-=can;a[cur]-=can;
		}cur=cur%n+1;
	}
	int res=1;
	for(int i=1;i<=n;i++)res=1ll*(((tmp[i]-a[i])^(1ll*i*i))+1)%MOD*res%MOD;
	printf("%d\n",res);
	return 0;
}