POI2014 Card
阿新 • • 發佈:2018-12-11
Card
POI2014
題意
1.有n張卡片,第i張卡片上,正面的數為a[i],反面的數為b[i]。
2.有m個操作,第i個操作會交換c[i]和d[i]兩個位置上的卡片
3.每個操作完成後,需要判斷,通過任意翻轉卡片(把正面變為反面或把反面變成正面,但不能改變卡片的位置),能否讓卡片正面上的數從左到右單調不降。
解
1.如果沒有交換操作,那麼就是一個線性遞推
2.但是需要多次修改,考慮用線段樹維護
3.合併兩個線段,如果這兩個線段本身都是單調不降的,那麼只要合併處(左邊線段的右端點,右邊線段的左端點)單調不降,那麼整體就是單調不降了
4.每次合併時複雜度為
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;
}