题意:
有n张卡片在桌上一字排开,每张卡片上有两个数,第i张卡片上,正面的数为a[i],反面的数为b[i]。有m个操作,第i个操作会交换c[i]和d[i]两个位置上的卡片。每次操作后,你都需要判断,通过任意翻转卡片(把正面变为反面或把反面变成正面,但不能改变卡片的位置),能否让卡片正面上的数从左到右单调不降。n≤200000,m≤1000000。
题解:
线段树每个节点维护对应区间若第一张卡片为较小一面得到的最后一张卡片的最小值以及若第一张卡片为较大一面得到的最后一张卡片的最小值(如果无法则为-1)。操作就对于普通线段树的单点修改。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 200010 7 using namespace std; 8 9 inline int read(){ 10 char ch=getchar(); int f=1,x=0; 11 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 12 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 13 return f*x; 14 } 15 int a[maxn],b[maxn],l[maxn*3],r[maxn*3],mn[maxn*3],mx[maxn*3],n,m; 16 void update(int x){ 17 int lc=x<<1,rc=x<<1|1; 18 if(mn[lc]!=-1&&mn[rc]!=-1&&mn[lc]<=a[l[rc]])mn[x]=mn[rc]; 19 else if(mn[lc]!=-1&&mx[rc]!=-1&&mn[lc]<=b[l[rc]])mn[x]=mx[rc];else mn[x]=-1; 20 if(mx[lc]!=-1&&mn[rc]!=-1&&mx[lc]<=a[l[rc]])mx[x]=mn[rc]; 21 else if(mx[lc]!=-1&&mx[rc]!=-1&&mx[lc]<=b[l[rc]])mx[x]=mx[rc];else mx[x]=-1; 22 } 23 void build(int x,int bl,int br){ 24 l[x]=bl; r[x]=br; if(bl==br){mn[x]=a[bl]; mx[x]=b[bl]; return;} 25 int mid=(bl+br)>>1; build(x<<1,bl,mid); build(x<<1|1,mid+1,br); update(x); 26 } 27 void modify(int x,int pos){ 28 if(l[x]==r[x]){mn[x]=a[pos]; mx[x]=b[pos]; return;} int mid=(l[x]+r[x])>>1; 29 if(pos<=mid)modify(x<<1,pos);else modify(x<<1|1,pos); update(x); 30 } 31 int main(){ 32 n=read(); inc(i,1,n){a[i]=read(); b[i]=read(); if(a[i]>b[i])swap(a[i],b[i]);} 33 build(1,1,n); m=read(); 34 inc(i,1,m){ 35 int c=read(),d=read(); int t1=a[c],t2=b[c]; 36 a[c]=a[d]; b[c]=b[d]; modify(1,c); a[d]=t1; b[d]=t2; modify(1,d); 37 if(mn[1]==-1&&mx[1]==-1)puts("NIE");else puts("TAK"); 38 } 39 return 0; 40 }
20161110