p.s.:图没截全,出题人回复了我一个 ^_^
。
形式化题意(将询问转化成了求关键步骤而并非原询问,需要将该关键步骤的答案经过简单处理才能得到原询问答案)
你有两个 串 和 ,下标从 开始,初始均为 .
对于 串有 个操作,第 次操作 为:
- ,把 赋值成 。
对于 串也有 个操作,第 次操作 为:
- ,把 赋值成 。
你还有 个询问,对于第 次询问 ,先执行操作 和 (前 次询问执行的操作仍然会被保留),再求 和 左起(即以 为起点)最长的连续的 的长度 和 。
,,。
前置知识:珂朵莉树。
上面的题意已经转化得差不多了。看到区间赋值的操作,当然要想起珂愛的珂朵莉树,这是珂朵莉树最基础的操作之一。
其次考虑如何求左起最长连续的 的长度,因为是左起,所以区间长度就是右端点编号。可以从 set
的 begin
开始遍历直到 set
的 end
,一旦遍历到 的颜色段,就将答案设置成当前区间的左端点 (因为 是最后一个 的位置)。
然后你会发现:
因为每次从头开始遍历效率太低,珂朵莉树本身就是一种暴力数据结构,根本不能接受。
注意到只有区间赋值成 的操作,这意味着 和 单调非降,因此只需要从上一次找到的右端点 往后找就行了,具体实现方法是从 split(ans+1)
的迭代器( 是上一次询问的答案)开始遍历。
时间复杂度为 ,空间复杂度为 ,可以接受~~(这么珂愛的珂朵莉谁不会接受呢?)~~。
点击查看代码
#pragma GCC optimize("-fdelete-null-pointer-checks,inline-functions-called-once,-funsafe-loop-optimizations,-fexpensive-optimizations,-foptimize-sibling-calls,-ftree-switch-conversion,-finline-small-functions,inline-small-functions,-frerun-cse-after-loop,-fhoist-adjacent-loads,-findirect-inlining,-freorder-functions,no-stack-protector,-fpartial-inlining,-fsched-interblock,-fcse-follow-jumps,-fcse-skip-blocks,-falign-functions,-fstrict-overflow,-fstrict-aliasing,-fschedule-insns2,-ftree-tail-merge,inline-functions,-fschedule-insns,-freorder-blocks,-fwhole-program,-funroll-loops,-fthread-jumps,-fcrossjumping,-fcaller-saves,-fdevirtualize,-falign-labels,-falign-loops,-falign-jumps,unroll-loops,-fsched-spec,-ffast-math,Ofast,inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2", 3)
#pragma GCC target("avx","sse2")
//卡常
#include<bits/stdc++.h>
#define N 100005
#define lim 1000000000
using namespace std;
int n;
struct tool{
int l,r;
}a[N],b[N];
struct node{
int l,r,v;
bool operator<(const node&b)const{
return l<b.l;
}
};
struct odt{
set<node>s;
int ans;
auto split(int x){
if(x>lim){
return s.end();
}
auto it=s.lower_bound({x,0,0});
if(it!=s.end()&&it->l==x){
return it;
}
node t=*(--it);
s.erase(it);
s.insert({t.l,x-1,t.v});
return s.insert({x,t.r,t.v}).first;
}
void assign(int l,int r){
auto qr=split(r+1),ql=split(l);
s.erase(ql,qr);
s.insert({l,r,1});
}
int query(){
for(auto it=split(ans+1);it!=s.end();++it){
if(!it->v){
return ans=it->l-1;
}
}
return ans=lim;
}
}sa,ya;
int main(){
sa.s.insert({1,lim,sa.ans=0});
ya.s.insert({1,lim,ya.ans=0});
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d",&a[i].l,&a[i].r);
}
for(int i=1;i<=n;++i){
scanf("%d%d",&b[i].l,&b[i].r);
}
for(int i=1;i<=n;++i){
sa.assign(a[i].l,a[i].r);
ya.assign(b[i].l,b[i].r);
int u=sa.query(),v=ya.query();
if(u>v){
cout<<"sa_win!\n"<<u-v<<'\n';
}else if(u^v){
cout<<"ya_win!\n"<<v-u<<'\n';
}else{
cout<<"win_win!\n0\n";
}
}
}