CF911G Mass Change Queries 题解 && [国家集训队]middle 题解
CF911G 题意: 给定长度为 n 的序列,m 次操作,每次操作形如 l r x y ,将 [l,r] 内等于 $x$ 的数替换成 y 。( n,m ≤ 2e5, ai ≤ 100 ) 最后询问整个序列什么样子
因为 $a_i \leq 100$ ,所以开100棵动态开点权值线段树, 每次操作将第 x 棵线段树上的 [l,r] 区间拆下来,和第 y 棵线段树上的 [l,r] 区间合并。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn=4e5+3; 6 7 int rt[101], rub[maxn<<5], top; 8 9 struct SegmenTree{ 10 11 int lson,rson, sum; 12 13 };SegmenTree tree[maxn<<2]; 14 15 int read() 16 { 17 int ret=0, f=1; char x=getchar(); 18 19 while(!isdigit(x)) { if(x=='-') f=-1; x=getchar(); } 20 21 while(isdigit(x)) { ret=ret*10+x-'0'; x=getchar(); } 22 23 return ret*f; 24 } 25 26 int lson[maxn<<5], rson[maxn<<5], sum[maxn<<5], cnt, n; 27 28 int newnode() { return top? rub[top--] : ++cnt;} 29 30 void delnode(int &x) { lson[x]=rson[x]=sum[x]=0; rub[++top]=x; x=0; } 31 32 void pushup(int p) { sum[p]=sum[lson[p]]+sum[rson[p]]; } 33 34 void Modify(int &p,int x,int l,int r) 35 { 36 if(!p) p=newnode(); 37 38 if(l==r) { sum[p]=1; return; } 39 40 int mid=(l+r)>>1; 41 42 if(x<=mid) Modify(lson[p],x,l,mid); 43 44 else Modify(rson[p],x,mid+1,r); 45 46 pushup(p); 47 } 48 49 void merge(int &x,int &y,int l,int r) 50 { 51 if(!x||!y) { x+=y; return; } 52 53 if(l==r) { sum[x]+=sum[y]; delnode(y); } 54 55 int mid=(l+r)>>1; 56 57 merge(lson[x],lson[y],l,mid); 58 59 merge(rson[x],rson[y],mid+1,r); 60 61 delnode(y); pushup(x); 62 } 63 64 void split(int &x,int &y,int ql,int qr,int l,int r) 65 { 66 if(!y) return; 67 68 if(ql<=l&&r<=qr) { x=y; y=0; return; } 69 70 if(!x) x=newnode(); 71 72 int mid=(l+r)>>1; 73 74 if(ql<=mid) split(lson[x],lson[y],ql,qr,l,mid); 75 76 if(qr>mid) split(rson[x],rson[y],ql,qr,mid+1,r); 77 78 pushup(x); pushup(y); 79 } 80 81 int a[maxn]; 82 83 void dfs(int p,int clr,int l,int r) 84 { 85 if(l==r) { a[l]=(sum[p]?clr:a[l]); return; } 86 87 int mid=(l+r)>>1; 88 89 dfs(lson[p],clr,l,mid); dfs(rson[p],clr,mid+1,r); 90 } 91 92 int main() 93 { 94 n=read(); 95 96 for(int i=1;i<=n;i++) { a[i]=read(); Modify(rt[a[i]],i,1,n); } 97 98 int Q=read(); 99 100 while(Q--) 101 { 102 int l=read(),r=read(),x=read(),y=read(); 103 104 if(x!=y) { int tmp=0; split(tmp,rt[x],l,r,1,n); merge(rt[y],tmp,1,n); } 105 } 106 107 for(int i=1;i<=100;i++) dfs(rt[i],i,1,n); 108 109 for(int i=1;i<=n;++i) printf("%d ",a[i]); 110 }
middle 题意:给定长度为 n 的序列 , 每次询问左端点在 [a,b] 右端点在[c,d] 的子段的中位数最大是多少。强制在线 (n,m≤1e5, ai ≤ 1e9)
有个显然的思路: 二分一个mid,将所有大于等于 amid 的数设为 1 ,小于他的设为-1。如果该区间的和大于等于0,mid应该更大,反之应该更小。
因为[b,c] 区间的数肯定要选,所以只要对于每个mid,在[a,b] 选个最大后缀,在[c,d] 选个最大前缀即可。
那么我们可以选择建立mid个线段树,分别表示中位数为 mid 时的(1,-1)序列。但是空间开不下。
我们观察到,改变一个mid时,只有一个数从1变成了-1,所以我们选择开可持久化线段树。时间 O(nlog^2n) 空间O(nlogn)
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+3; int read() { int ret=0, f=1; char x=getchar(); while(!isdigit(x)) { if(x=='-') f=-1; x=getchar(); } while(isdigit(x)) { ret=ret*10+x-'0'; x=getchar(); } return ret*f; } struct node{ int lmax,rmax,sum,mx; void Init() { lmax=rmax=-maxn, sum=0; } }; node Ans; struct SegmenTree_Persistence{ int lson, rson; node dat; };SegmenTree_Persistence tree[maxn<<5]; node merge(node x, node y) { node ret; ret.lmax=max(x.lmax, x.sum+y.lmax); ret.rmax=max(y.rmax, y.sum+x.rmax); ret.sum=x.sum+y.sum; return ret; } int root[maxn],a[maxn],id[maxn],cnt; void build(int &index,int l,int r) { index=++cnt; tree[index].dat.lmax=tree[index].dat.rmax=tree[index].dat.sum=r-l+1; if(l==r) { return ; } int mid=(l+r)>>1; build(tree[index].lson,l,mid); build(tree[index].rson,mid+1,r); } void Modify(int &index,int l,int r,int p) { tree[++cnt]=tree[index]; index=cnt; if(l==r) { tree[index].dat.lmax=tree[index].dat.rmax=tree[index].dat.sum=-1; return ; } int mid=(l+r)>>1; if(p<=mid) Modify(tree[index].lson,l,mid,p); else Modify(tree[index].rson,mid+1,r,p); tree[index].dat=merge(tree[tree[index].lson].dat, tree[tree[index].rson].dat); } void Query(int index,int l,int r,int ql,int qr) { if(ql<=l&&qr>=r) { Ans=merge(Ans, tree[index].dat); return ; } int mid=(l+r)>>1; if(ql<=mid) Query(tree[index].lson, l, mid, ql, qr); if(qr>mid) Query(tree[index].rson, mid+1, r, ql, qr); } int q[10], n; int check(int mid) { int val=0; if(q[2]+1<=q[3]-1) Ans.Init(), Query(root[mid],1,n,q[2]+1,q[3]-1), val+=Ans.sum; Ans.Init(), Query(root[mid],1,n,q[1],q[2]), val+=Ans.rmax; Ans.Init(), Query(root[mid],1,n,q[3],q[4]), val+=Ans.lmax; return val>=0; } int ans; int cmp(int x,int y) { return a[x]<a[y]; } int main() { n=read(); build(root[1],1,n); tree[0].dat.Init(); for(int i=1;i<=n;i++) a[i]=read(), id[i]=i; sort(id+1,id+n+1,cmp); // for(int i=1;i<=n;i++) { cerr << id[i] << " "; } cerr<<endl; for(int i=2;i<=n;i++) root[i]=root[i-1], Modify(root[i],1,n,id[i-1]); int m=read(); while(m--) { for(int i=1;i<=4;i++) q[i]=(read()+ans)%n+1; sort(q+1,q+4+1); // for(int i=1;i<=4;i++) { cerr << q[i] << " "; } cerr << endl ; int l=1,r=n; while(l<=r) { int mid=(l+r)>>1; if(check(mid)) ans=a[id[mid]], l=mid+1; else r=mid-1; } printf("%d\n",ans); } }
浙公网安备 33010602011771号