AT_agc064_c [AGC064C] Erase and Divide Game 题解

先考虑所有 \(l_i=r_i\) 时怎么做,可以建出反向 Trie 树,问题转化为从根开始每次向左子树或右子树走,第一个拿到空子树的人输,直接在 Trie 上 dp 即可。

考虑从叶子层开始对每一层的点合并两个子树的 dp 值,发现每一层值相同的连续段是较少的。于是可以维护这些连续段,每次合并要将每个 \(f_i\)\(f_{i+2^k}\) 合并,把连续段分成两部分双指针合并即可,时间复杂度 \(\mathcal O(N\log V)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
struct node{
	ll l,r;int x;
};
node operator+(node x,node y){
	return {max(x.l,y.l),min(x.r,y.r),x.x!=-1||y.x!=-1?x.x!=1||y.x!=1:-1};
}
int T,n;
vector<node>s,s1,s2;
signed main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n); 
		ll l,r,ls=0;
		s.clear();
		rept(i,0,n){
			scanf("%lld%lld",&l,&r);
			if(ls<l)s.pb({ls,l,-1});
			s.pb({l,r+1,1}),ls=r+1;
		}
		s.pb({ls,1ll<<60,-1});
		drep(i,59,0){
			ll k=1ll<<i;
			s1.clear(),s2.clear();
			for(node i:s){
				if(i.r<=k)s1.pb(i);
				else if(i.l>=k)s2.pb({i.l-k,i.r-k,i.x});
				else s1.pb({i.l,k,i.x}),s2.pb({0,i.r-k,i.x});
			}
			s.clear();
			for(auto i=s1.begin(),j=s2.begin();i!=s1.end()&&j!=s2.end();){
				ll r=min(i->r,j->r);
				s.pb(*i+*j);
				if(i->r==r)i++;
				if(j->r==r)j++;
			}
		}
		puts(s[0].x?"Takahashi":"Aoki");
	}
	return 0;
}
posted @ 2024-10-22 16:33  zifanwang  阅读(4)  评论(0编辑  收藏  举报