Card
POI2014
题意
1.有n张卡片,第i张卡片上,正面的数为a[i],反面的数为b[i]。
2.有m个操作,第i个操作会交换c[i]和d[i]两个位置上的卡片
3.每个操作完成后,需要判断,通过任意翻转卡片(把正面变为反面或把反面变成正面,但不能改变卡片的位置),能否让卡片正面上的数从左到右单调不降。
解
1.如果没有交换操作,那么就是一个线性递推
2.但是需要多次修改,考虑用线段树维护
3.合并两个线段,如果这两个线段本身都是单调不降的,那么只要合并处(左边线段的右端点,右边线段的左端点)单调不降,那么整体就是单调不降了
4.每次合并时复杂度为 2 4 2^4 24
5.把交换操作看做两个单点更新
6.查询就是整个区间是否存在合法的方案
具体代码
#include<bits/stdc++.h>
using namespace std;
const int M=200005;
int n,m,A[M][2];
struct Tree {
bool dp[2][2];
int L[2],R[2];
} tree[M*4];
void Up(Tree &rt,Tree Ls,Tree Rs) {
rt.L[0]=Ls.L[0],rt.L[1]=Ls.L[1];
rt.R[0]=Rs.R[0],rt.R[1]=Rs.R[1];
for(int i=0; i<2; i++) {
for(int j=0; j<2; j++) {
if(Ls.dp[i][0]&&Rs.dp[0][j]&&Ls.R[0]<=Rs.L[0]){
rt.dp[i][j]=1;
continue;
}
if(Ls.dp[i][0]&&Rs.dp[1][j]&&Ls.R[0]<=Rs.L[1]){
rt.dp[i][j]=1;
continue;
}
if(Ls.dp[i][1]&&Rs.dp[0][j]&&Ls.R[1]<=Rs.L[0]){
rt.dp[i][j]=1;
continue;
}
if(Ls.dp[i][1]&&Rs.dp[1][j]&&Ls.R[1]<=Rs.L[1]){
rt.dp[i][j]=1;
continue;
}
rt.dp[i][j]=0;
}
}
}
void build(int L,int R,int p) {
if(L==R) {
tree[p].L[0]=tree[p].R[0]=A[L][0];
tree[p].L[1]=tree[p].R[1]=A[L][1];
tree[p].dp[0][0]=tree[p].dp[1][1]=1;
tree[p].dp[0][1]=tree[p].dp[1][0]=0;
return ;
}
int mid=L+R>>1;
build(L,mid,p<<1);
build(mid+1,R,p<<1|1);
Up(tree[p],tree[p<<1],tree[p<<1|1]);
}
void update(int x,int L,int R,int p) {
if(L==R) {
tree[p].L[0]=tree[p].R[0]=A[x][0];
tree[p].L[1]=tree[p].R[1]=A[x][1];
tree[p].dp[0][0]=tree[p].dp[1][1]=1;
tree[p].dp[0][1]=tree[p].dp[1][0]=0;
return ;
}
int mid=L+R>>1;
if(x<=mid)update(x,L,mid,p<<1);
else update(x,mid+1,R,p<<1|1);
Up(tree[p],tree[p<<1],tree[p<<1|1]);
}
int main() {
int x,y;
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%d %d",&A[i][0],&A[i][1]);
}
build(1,n,1);
scanf("%d",&m);
for(int i=1; i<=m; i++) {
scanf("%d %d",&x,&y);
swap(A[x][0],A[y][0]);
swap(A[x][1],A[y][1]);
update(x,1,n,1);
update(y,1,n,1);
bool flag=0;
for(int i=0; i<2; i++) {
for(int j=0; j<2; j++) {
if(tree[1].dp[i][j])flag=1;
}
}
if(flag)printf("TAK\n");
else printf("NIE\n");
}
return 0;
}