• 【bzoj3526】[Poi2014]Card 线段树区间合并


    题目描述

    有n张卡片在桌上一字排开,每张卡片上有两个数,第i张卡片上,正面的数为a[i],反面的数为b[i]。现在,有m个熊孩子来破坏你的卡片了!
    第i个熊孩子会交换c[i]和d[i]两个位置上的卡片。
    每个熊孩子捣乱后,你都需要判断,通过任意翻转卡片(把正面变为反面或把反面变成正面,但不能改变卡片的位置),能否让卡片正面上的数从左到右单调不降。

    输入

    第一行一个n。
    接下来n行,每行两个数a[i],b[i]。
    接下来一行一个m。
    接下来m行,每行两个数c[i],d[i]。

    输出

    m行,每行对应一个答案。如果能成功,输出TAK,否则输出NIE。

    样例输入

    4
    2 5
    3 4
    6 3
    2 7
    2
    3 4
    1 3

    样例输出

    NIE
    TAK


    题解

    线段树区间合并

    当然这题没有指定区间查询,所以好做很多。

    设f[x][0/1][0/1]表示区间x左端点为a/b,右端点为a/b,能否组成不下降序列。

    那么区间合并时判断一下中间的大小关系即可。

    注意初始化叶子结点时应该只把f[x][0][0]和f[x][1][1]赋成true。

    代码不忍直视。。。可能使用for循环0/1会好一些。

    #include <cstdio>
    #include <algorithm>
    #define N 200010
    #define lson l , mid , x << 1
    #define rson mid + 1 , r , x << 1 | 1
    using namespace std;
    int a[N] , b[N];
    bool f[N << 2][2][2];
    void pushup(int l , int r , int x)
    {
    	int mid = (l + r) >> 1 , ls = x << 1 , rs = x << 1 | 1;
    	f[x][0][0] = f[x][0][1] = f[x][1][0] = f[x][1][1] = 0;
    	if(a[mid] <= a[mid + 1]) f[x][0][0] |= f[ls][0][0] & f[rs][0][0] , f[x][0][1] |= f[ls][0][0] & f[rs][0][1] , f[x][1][0] |= f[ls][1][0] & f[rs][0][0] , f[x][1][1] |= f[ls][1][0] & f[ls][0][1];
    	if(a[mid] <= b[mid + 1]) f[x][0][0] |= f[ls][0][0] & f[rs][1][0] , f[x][0][1] |= f[ls][0][0] & f[rs][1][1] , f[x][1][0] |= f[ls][1][0] & f[rs][1][0] , f[x][1][1] |= f[ls][1][0] & f[ls][1][1];
    	if(b[mid] <= a[mid + 1]) f[x][0][0] |= f[ls][0][1] & f[rs][0][0] , f[x][0][1] |= f[ls][0][1] & f[rs][0][1] , f[x][1][0] |= f[ls][1][1] & f[rs][0][0] , f[x][1][1] |= f[ls][1][1] & f[ls][0][1];
    	if(b[mid] <= b[mid + 1]) f[x][0][0] |= f[ls][0][1] & f[rs][1][0] , f[x][0][1] |= f[ls][0][1] & f[rs][1][1] , f[x][1][0] |= f[ls][1][1] & f[rs][1][0] , f[x][1][1] |= f[ls][1][1] & f[ls][1][1];
    }
    void build(int l , int r , int x)
    {
    	if(l == r)
    	{
    		f[x][0][0] = f[x][1][1] = 1;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(lson) , build(rson);
    	pushup(l , r , x);
    }
    void update(int p , int c , int d , int l , int r , int x)
    {
    	if(l == r)
    	{
    		a[p] = c , b[p] = d;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(p <= mid) update(p , c , d , lson);
    	else update(p , c , d , rson);
    	pushup(l , r , x);
    }
    int main()
    {
    	int n , m , i , x , y , s , t;
    	scanf("%d" , &n);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d%d" , &a[i] , &b[i]);
    	build(1 , n , 1);
    	scanf("%d" , &m);
    	while(m -- )
    	{
    		scanf("%d%d" , &x , &y);
    		s = a[x] , t = b[x] , update(x , a[y] , b[y] , 1 , n , 1) , update(y , s , t , 1 , n , 1);
    		printf("%s
    " , f[1][0][0] | f[1][0][1] | f[1][1][0] | f[1][1][1] ? "TAK" : "NIE");
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    NABCD项目分析
    第七周学习进度
    第六周学习进度
    构建之法阅读笔记03
    [算法] 求x的n次方的一种for循环实现
    [算法]分解质因数
    通过索引操作数组
    [swift入门] 数据类型
    二叉排序树 常用函数小结
    剑指 Offer 54. 二叉搜索树的第k大节点 做题小结
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7054735.html
Copyright © 2020-2023  润新知