POI2014 Card

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值