1. 程式人生 > >POI2014 Card

POI2014 Card

Card

POI2014

題意

1.有n張卡片,第i張卡片上,正面的數為a[i],反面的數為b[i]。

2.有m個操作,第i個操作會交換c[i]和d[i]兩個位置上的卡片

3.每個操作完成後,需要判斷,通過任意翻轉卡片(把正面變為反面或把反面變成正面,但不能改變卡片的位置),能否讓卡片正面上的數從左到右單調不降。

1.如果沒有交換操作,那麼就是一個線性遞推

2.但是需要多次修改,考慮用線段樹維護

3.合併兩個線段,如果這兩個線段本身都是單調不降的,那麼只要合併處(左邊線段的右端點,右邊線段的左端點)單調不降,那麼整體就是單調不降了

4.每次合併時複雜度為242^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; }