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 }
CF911G

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);
    }
}
middle

 

posted @ 2021-05-06 14:17  Van-Helsing  阅读(97)  评论(0)    收藏  举报