NKOJ 3777 卡牌操作(线段树)

该博客详细介绍了如何利用图论思想和线段树解决NKOJ 3777卡牌操作问题。通过对卡牌建立边连接,转化为寻找路径的存在性,然后通过线段树来维护区间内的连通性。博主讨论了线段树节点的更新和查询方法,以确定从起始点到终点是否存在路径,并给出了相关代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P3777卡牌操作

问题描述

   有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

提示

【样例解释】
交换3和4后,卡片序列为(2,5) (3,4) (2,7) (6,3),不能成功。
交换1和3后,卡片序列为(2,7) (3,4) (2,5) (6,3),翻转第3张卡片,卡片的正面为2,3,5,6,可以成功。

n≤200000,m≤1000000,0≤a[i],b[i]≤10000000,1≤c[i],d[i]≤n.

首先用图论的思想,将一个卡牌看成两个点AiBi,不妨设Ai<=Bi,如果Ai的值小于Ai+1,那么就连一条从Ai指向Ai+1的边,同理处理所有点。
那么是否有解的问题就变成了问是否存在一条路径可以从1走到n。

然后用到线段树维护连通性。

我们讨论一个区间[L,R],用Va表示从AL出发能够到达R号点时最小的权值,Vb表示从BL出发。
那么我们ls为区间的左儿子,rs为右儿子,那么我们需要考虑是否能通过左右儿子的值来算出[L,R]的值。令mid为L+R>>1

首先我们考虑算出Va[L,R]
那么如果Vals<=Amid+1,那么意味着从mid点可以连到Amid+1,所以Va[L,R]=Vars
否则讨论Vals<=Bmid+1,成立就意味着从mid点可以连到Bmid+1,所以Va[L,R]=Vbrs
如果都不满足,那么mid不能连到mid+1,意味着不存在一条从ALARBR的路径,那么Va[L,R]=inf

Vb[LR]同理计算。
最后只需要看Va[1,n]Vb[1,n]是否存在就行了。

关于修改显然就是单点修改了。具体可以参考代码。


代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#define N 222222
#define M 2222222
using namespace std;
int n,A[N],B[N],m;
int ls[M],rs[M],va[M],vb[M],tot;
void UD(int p,int l,int r)
{
    int mid=(l+r>>1)+1;
    if(va[ls[p]]<=A[mid])va[p]=va[rs[p]];
    else if(va[ls[p]]<=B[mid])va[p]=vb[rs[p]];
    else va[p]=1e9;
    if(vb[ls[p]]<=A[mid])vb[p]=va[rs[p]];
    else if(vb[ls[p]]<=B[mid])vb[p]=vb[rs[p]];
    else vb[p]=1e9;
}
int BT(int x,int y)
{
    int p=++tot;
    if(x<y)
    {
        int mid=x+y>>1;
        ls[p]=BT(x,mid);
        rs[p]=BT(mid+1,y);
        UD(p,x,y);
    }
    else va[p]=A[x],vb[p]=B[x];
    return p;
}
void CHA(int p,int l,int r,int k)
{
    if(l==r){va[p]=A[l];vb[p]=B[l];return;}
    int mid=l+r>>1;
    if(k<=mid)CHA(ls[p],l,mid,k);
    else CHA(rs[p],mid+1,r,k);
    UD(p,l,r);
}
int main()
{
    int i,x,y;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&A[i],&B[i]);
        if(A[i]>B[i])swap(A[i],B[i]);
    }
    BT(1,n);
    scanf("%d",&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        swap(A[x],A[y]);
        swap(B[x],B[y]);
        CHA(1,1,n,x);
        CHA(1,1,n,y);
        if(va[1]!=1e9||vb[1]!=1e9)puts("TAK");
        else puts("NIE");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值