线段树
对于两个区间,如果我们知道前一个区间的开头卡片的不同选择(正面或反面)的结果,和后一个区间开头不同选择的结果,那么我们肯定能将他们合并,从而得到整个区间开头不同选择的结果。
考虑线段树。f[i][0 / 1]表示i区间以正面/反面开头的单调不降序列是否存在,不存在记-1,存在就记最后一个卡片最小能选正面还是反面
#include<cstdio>
#include<algorithm>
#define N 200005
using namespace std;
int l[N*5], r[N*5], f[N*5][2], a[N][2];
void merge(int x)
{
f[x][0]=f[x][1]=-1;
for(int k = 0; k <= 1; k++)
if(f[x<<1][k]!=-1)
{
int pos=f[x<<1][k];
if(a[l[x<<1|1]][0]>=a[r[x<<1]][pos] && f[x<<1|1][0]!=-1)f[x][k]=f[x<<1|1][0];
else if(a[l[x<<1|1]][1]>=a[r[x<<1]][pos] && f[x<<1|1][1]!=-1)f[x][k]=f[x<<1|1][1];
}
}
void build(int x, int left, int right)
{
l[x]=left;
r[x]=right;
if(left==right)
{
f[x][0]=0;
f[x][1]=(a[left][0]==a[left][1]?0:1);
return;
}
int mid=(left+right)>>1;
build(x<<1,left,mid);
build(x<<1|1,mid+1,right);
merge(x);
}
void update(int x, int pos)
{
if(l[x]==r[x])
{
f[x][0]=0;
f[x][1]=(a[l[x]][0]==a[l[x]][1]?0:1);
return;
}
int mid=(l[x]+r[x])>>1;
if(pos<=mid)update(x<<1,pos);
else update(x<<1|1,pos);
merge(x);
}
int main()
{
int n, m;
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
scanf("%d%d",&a[i][0],&a[i][1]);
if(a[i][0]>a[i][1])swap(a[i][0],a[i][1]);
}
build(1,1,n);
scanf("%d",&m);
for(int i = 1; i <= m; i++)
{
int x, y;
scanf("%d%d",&x,&y);
swap(a[x],a[y]);
update(1,x);
update(1,y);
puts(f[1][0]!=-1?"TAK":"NIE");
}
return 0;
}