AtCoder Beginner Contest 255 G,H

G
看到有关Nim的题目,我们自然可以想到SG函数。

但是这里暴力求每个数的SG函数是不行的。

但是注意到很多数其实没有任何限制(即可以转移到任何点),那么它的SG函数值就是max(SG)+1,这意味着,我们可以维护一个区间,(l,r,fst),表示[l,r]区间内的SG函数是递增的,相邻两项差为1,且首项是fst。而我们最多有M个点是有转移限制的(即有些数转移不到),故这样的区间数为O(M)级别的。

同时,我们也处理有限制的区间,具体的,我们可以用一个map,设为cnt,储存SG=i的个数。但是SG函数的最大值可能到达1e18,所以仍然不行。但是由于只有O(M)个特殊点,所以cnt(x)2x最多只有O(M)个。故我们将cnt存储的数的值都减1,这样map中就只有O(M)个元素了。

前面说的有些抽象,具体看程序,下面是一个时间复杂度为O(Mlog2M)的实现:

#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl

using ll=long long;

const int maxn=200005;
int n,m;
ll a[maxn];
ll x[maxn],y[maxn];
std::set<std::array<ll,3>> seg;
std::map<ll,std::vector<ll>> v;

ll SG(ll number) {
	std::set<std::array<ll,3>>::iterator it=seg.lower_bound({number+1ll,-1ll,-1ll});
	it--;
	return number-(*it)[0]+(*it)[2];
}

int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) {
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=m;i++) {
		scanf("%lld%lld",&x[i],&y[i]);
		v[x[i]].push_back(x[i]-y[i]);
	}
	std::map<ll,int> cnt;
	ll pre=0,sg=-1;
	for(auto item : v) {
		if(pre<item.first) seg.insert({pre,item.first-1ll,sg+1ll}),sg+=item.first-pre;
		ll newsg=sg+1ll;
		std::vector<ll> orr;
		for(auto number : item.second) {
			ll rec=SG(number);			
			if(cnt[rec]==0) newsg=std::min(newsg,rec);
			else cnt[rec]--,orr.push_back(rec); 
		}
		seg.insert({item.first,item.first,newsg});
		if(newsg<=sg) cnt[newsg]++;
		else sg=newsg;
		pre=item.first+1;
		for(auto number : orr) {
			cnt[number]++;
		}
	}
	seg.insert({pre,(ll)1e18+5ll,sg+1ll}); 
	ll ans=0;
	for(int i=1;i<=n;i++) {
		ans^=SG(a[i]);
	}
	if(ans==0) printf("Aoki");
	else printf("Takahashi");
	return 0;
}

H
我们其实要维护三个操作:

  1. 区间赋值为0
  2. 区间加上等差数列
  3. 区间查询和

可以发现用动态开点线段树可以维护区间和,我们要对1,2分别设懒标记,下传它们就可以了。

等等,这样真的对吗?如果我们下传时,遇到儿子的懒标记,我们又如何处理呢?(因为当前点加上儿子点共有4个懒标记,是难以合并的)

我们可以考虑通过容斥去掉一个限制——我们不难发现,若之前我们没有进行操作1,那么我们可以通过当前时间直接算出区间和。那么我们只要减去区间内每个元素i最后一次操作1的时间t乘上i即可。

比如样例中第二个询问,我们首先计算没有第一次操作时的区间和=(3+4)×3=21,然后由于3最新一次赋值为0的时间为24最新一次赋值时间为0,故输出的答案要减去2×3=6,即为15

现在,我们就要实现如下操作:

  1. 区间赋值最新的时间
  2. 查询区间和

这样只需一个懒标记即可,时间、空间复杂度都是O(qlogn)

特别注意,这题取模很烦,不要漏掉取模,并且,如果你对时间取模,那么懒标记初始值一定要设为1,否则如果时间是998244343的倍数时,懒标记就是0,会出错。(最后6个点会WA,调试了好久才发现)

posted @   Nastia  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示