BZOJ 3526 [Poi2014]Card

线段树

对于两个区间,如果我们知道前一个区间的开头卡片的不同选择(正面或反面)的结果,和后一个区间开头不同选择的结果,那么我们肯定能将他们合并,从而得到整个区间开头不同选择的结果。

考虑线段树。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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值