• 树套树


    跟随着潮流,弱弱的学习了一下主席树。明白了思想之后,第一次比较快的自己写出了代码。小专题

    cogs930找第K小的数||1534K大数

    题目大意:静态区间第K小的查询。

    思路:裸裸的主席树模板题。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    struct Node{
        Node *ch[2];
        int a,siz;
        Node (){ch[0]=ch[1]=NULL;siz=a=0;}
        void updata(){
            siz=a;
            if (ch[0]!=NULL) siz+=ch[0]->siz;
            if (ch[1]!=NULL) siz+=ch[1]->siz;
        }
    }*null=new Node(),*root[100001]={NULL},q[2000001];
    int a[100001]={0},a2[100001]={0},q_size=0;
    void insert(Node *&y,Node *&x,int l,int r,int xx)
    {
        int mid;
        if (x==NULL) x=null;
        y=&q[++q_size];
        *y=Node();
        if (l==r)
        {
            *y=*x;
            y->siz++;y->a++;
            return;
        }
        mid=(l+r)/2;
        if (xx<=a2[mid])
        {
            insert(y->ch[0],x->ch[0],l,mid,xx);
            y->ch[1]=x->ch[1];
            y->updata();
        }
        else
        {
            insert(y->ch[1],x->ch[1],mid+1,r,xx);
            y->ch[0]=x->ch[0];
            y->updata();
        }
    }
    void find(Node *&x2,Node *&x1,int l,int r,int k)
    {
        int ss=0,mid;
        if (x2==NULL) x2=null;
        if (x1==NULL) x1=null;
        if (l==r)
        {
            printf("%d
    ",a2[l]);
            return;
        }
        mid=(l+r)/2;
        if (x2->ch[0]!=NULL) ss+=x2->ch[0]->siz;
        if (x1->ch[0]!=NULL) ss-=x1->ch[0]->siz;
        if (ss>=k) find(x2->ch[0],x1->ch[0],l,mid,k);
        else find(x2->ch[1],x1->ch[1],mid+1,r,k-ss);
    }
    int main()
    {
        freopen("kth.in","r",stdin);
        freopen("kth.out","w",stdout);
        
        int n,m,i,j,k,l,r;
        null->ch[0]=null;null->ch[1]=null;
        scanf("%d%d",&n,&m);
        for (i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            a2[i]=a[i];
        }
        sort(a2+1,a2+n+1);
        int size;
        size=unique(a2+1,a2+n+1)-a2-1;
        for (i=1;i<=n;++i)
            insert(root[i],root[i-1],1,size,a[i]);
        for (i=1;i<=m;++i)
        {
            scanf("%d%d%d",&l,&r,&k);
            find(root[r],root[l-1],1,size,k);
        }
        
        fclose(stdin);
        fclose(stdout);
    }
    View Code

    cogs257动态排名系统

    题目大意:动态区间第K小的查询

    思路:裸裸的动态主席。。。在原来的主席树外面套一个树状数组,然后就可以了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<iostream>
    #include<cstring>
    using namespace std;
    struct use{
        int l,r,siz;
    }tree[10000001]={0};
    struct use1{
        int xx,yy,kk;
    }qq[10001]={0};
    int root[50001]={0},a[50001]={0},a2[60001]={0},q1[2000]={0},q2[2000]={0},tot=0,size,n;
    char ch[10001];
    int lowbit(int x){return x&-x;}
    void build(int &x,int l,int r)
    {
        int mid;
        tree[x=++tot].siz=0;
        if (l==r) return;
        mid=(l+r)/2;
        build(tree[x].l,l,mid);
        build(tree[x].r,mid+1,r);
    }
    void ins(int last,int &i,int l,int r,int x,int flag)
    {
        tree[i=++tot].siz=tree[last].siz+flag;
        tree[i].l=tree[last].l;tree[i].r=tree[last].r;
        if (l==r) return;
        int mid=(l+r)/2;
        if (x<=mid) ins(tree[last].l,tree[i].l,l,mid,x,flag);
        else ins(tree[last].r,tree[i].r,mid+1,r,x,flag);
    }
    void bit_ins(int i,int x,int flag)
    {
        for (;i<=n;i+=lowbit(i)) ins(root[i],root[i],1,size,x,flag);
    }
    int ask(int l,int r,int k)
    {
        int ss=0,i,j,mid;
        if (l==r) return l;
        for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].l].siz;
        for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].l].siz;
        mid=(l+r)/2;
        if (ss>=k)
        {
            for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l;
            for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l;
            return ask(l,mid,k);
        }
        else
        {
            for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r;
            for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r;
            return ask(mid+1,r,k-ss);
        }
    }
    int bit_ask(int l,int r,int k)
    {
        q1[0]=q2[0]=0;
        for (;l;l-=lowbit(l)) q1[++q1[0]]=root[l];
        for (;r;r-=lowbit(r)) q2[++q2[0]]=root[r];
        return ask(1,size,k);
    }
    int main()
    {
        freopen("dynrank.in","r",stdin);
        freopen("dynrank.out","w",stdout);
        
        int m,i,j,d;
        scanf("%d",&d);
        while(d)
        {
        scanf("%d%d",&n,&m);
        tot=0;a2[0]=0;
        memset(tree,0,sizeof(tree));
        memset(root,0,sizeof(root));
        for (i=1;i<=n;++i) 
        {
          scanf("%d",&a[i]);
          ++a2[0];a2[a2[0]]=a[i];
        }
        for (i=1;i<=m;++i)
        {
            scanf("%*c%c%d%d",&ch[i],&qq[i].xx,&qq[i].yy);
            if (ch[i]=='Q') scanf("%d",&qq[i].kk);
            else a2[++a2[0]]=qq[i].yy;
        }
        sort(a2+1,a2+a2[0]+1);
        size=unique(a2+1,a2+a2[0]+1)-a2-1;
        build(root[0],1,size);
        for (i=1;i<=n;++i)
        {
            a[i]=upper_bound(a2+1,a2+size+1,a[i])-a2-1;
            bit_ins(i,a[i],1);
        }
        for (i=1;i<=m;++i)
        {
            if (ch[i]=='Q')
            {
                j=bit_ask(qq[i].xx-1,qq[i].yy,qq[i].kk);
                printf("%d
    ",a2[j]);
            }
            else
            {
                bit_ins(qq[i].xx,a[qq[i].xx],-1);
                a[qq[i].xx]=upper_bound(a2+1,a2+size+1,qq[i].yy)-a2-1;
                bit_ins(qq[i].xx,a[qq[i].xx],1);
            }
        }
        --d;
        }
        
        fclose(stdin);
        fclose(stdout);
    }
    View Code

    cogs1715||bzoj3295动态逆序对

    题目大意:每次删除一个数,输出删除前逆序对的总个数。

    思路:用的动态主席,倒着来做,ask略有不同,然后很开心的re了,之后NU刷cogs。。。MLE了一片。。。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    struct use{
        int l,r,siz;
    }tree[10000000];
    int root[100001]={0},a[100001]={0},num[100001]={0},a2[100001]={0},del[50001]={0},q1[2000]={0},q2[2000]={0},
        cc[100001]={0},tot=0,n;
    long long ansi[50001]={0};
    int lowbit(int x) {return x&(-x);}
    int work(int l,int r)
    {
        int mid,ans=0,i,j;
        if (l==r) return ans;
        mid=(l+r)/2;
        ans+=work(l,mid)+work(mid+1,r);
        cc[0]=0;i=l;j=mid+1;
        while(i<=mid&&j<=r)
        {
            if (a2[i]<a2[j])
            {
                cc[++cc[0]]=a2[i];++i;
            }
            else
            {
                cc[++cc[0]]=a2[j];++j;
                ans+=mid-i+1;
            }
        }
        for (;i<=mid;++i) cc[++cc[0]]=a2[i];
        for (;j<=r;++j) cc[++cc[0]]=a2[j];
        for (i=l;i<=r;++i) a2[i]=cc[i-l+1];
        return ans;
    }
    void build(int &x,int l,int r)
    {
        int mid;
        tree[x=++tot].siz=0;
        if (l==r) return;
        mid=(l+r)/2;
        build(tree[x].l,l,mid);
        build(tree[x].r,mid+1,r);
    }
    void ins(int last,int &i,int l,int r,int x,int flag)
    {
        int mid;
        tree[i=++tot].siz=tree[last].siz+flag;
        tree[i].l=tree[last].l;tree[i].r=tree[last].r;
        if (l==r) return;
        mid=(l+r)/2;
        if (x<=mid) ins(tree[last].l,tree[i].l,l,mid,x,flag);
        else ins(tree[last].r,tree[i].r,mid+1,r,x,flag);
    }
    void bit_ins(int i,int x,int flag)
    {
        for (;i<=n;i+=lowbit(i)) ins(root[i],root[i],1,n,x,flag);
    }
    long long ask(int l,int r,int x,int flag)
    {
        long long ss=0;
        int mid,i;
        if (l==r) return 0;
        mid=(l+r)/2;
        if (flag==0)
        {
            ss=0;
            for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].l].siz;
            for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].l].siz;
            if (x<=mid) 
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l;
               return ask(l,mid,x,flag);
            }
            else 
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r;
               return ss+ask(mid+1,r,x,flag);
            }
        }
        else
        {
            ss=0;
            for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].r].siz;
            for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].r].siz;
            if (x<=mid)
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l;
               return ss+ask(l,mid,x,flag);
            }
            else
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r;
               return ask(mid+1,r,x,flag);
            }
        }
    }
    long long bit_ask(int l,int r,int x,int flag)
    {
        q1[0]=q2[0]=0;
        for (;l;l-=lowbit(l)) q1[++q1[0]]=root[l];
        for (;r;r-=lowbit(r)) q2[++q2[0]]=root[r];
        return ask(1,n,x,flag);
    }
    int main()
    {
        freopen("inverse.in","r",stdin);
        freopen("inverse.out","w",stdout);
        
        int m,i,j,k;
        long long ans=0;
        scanf("%d%d",&n,&m);
        for (i=1;i<=n;++i) 
        {
           scanf("%d",&a[i]);
           num[a[i]]=i;
        }
        for (i=m;i>=1;--i)
        {
            scanf("%d",&del[i]);
            a[num[del[i]]]=0;
        }
        build(root[0],1,n);
        for (i=1;i<=n;++i)
            if (a[i])
            {
               a2[++a2[0]]=a[i];
               bit_ins(i,a[i],1);
            }
        ans=work(1,a2[0]);
        for (i=1;i<=m;++i)
        {
            k=del[i];j=num[k];a[j]=k;
            bit_ins(j,k,1);
            if (j>1) ans+=bit_ask(0,j-1,k,1);
            if (j<n) ans+=bit_ask(j,n,k,0);
            ansi[i]=ans;
        }
        for (i=m;i>=1;--i) printf("%I64d
    ",ansi[i]);
        
        fclose(stdin);
        fclose(stdout);
    }
    80分code

    终于发现re的原因了,这题中每次插入一个点的时候都会新建很多节点,其实有的节点是可以直接用原来的就可以了。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    struct use{
        int l,r,siz;
    }tree[10000000];
    int root[100001]={0},a[100001]={0},num[100001]={0},a2[100001]={0},del[50001]={0},q1[2000]={0},q2[2000]={0},
        cc[100001]={0},tot=0,n;
    long long ansi[50001]={0};
    int lowbit(int x) {return x&(-x);}
    int work(int l,int r)
    {
        int mid,ans=0,i,j;
        if (l==r) return ans;
        mid=(l+r)/2;
        ans+=work(l,mid)+work(mid+1,r);
        cc[0]=0;i=l;j=mid+1;
        while(i<=mid&&j<=r)
        {
            if (a2[i]<a2[j])
            {
                cc[++cc[0]]=a2[i];++i;
            }
            else
            {
                cc[++cc[0]]=a2[j];++j;
                ans+=mid-i+1;
            }
        }
        for (;i<=mid;++i) cc[++cc[0]]=a2[i];
        for (;j<=r;++j) cc[++cc[0]]=a2[j];
        for (i=l;i<=r;++i) a2[i]=cc[i-l+1];
        return ans;
    }
    void build(int &x,int l,int r)
    {
        int mid;
        tree[x=++tot].siz=0;
        if (l==r) return;
        mid=(l+r)/2;
        build(tree[x].l,l,mid);
        build(tree[x].r,mid+1,r);
    }
    void ins(int last,int &i,int l,int r,int x,int flag)
    {
        int mid;
        if (!i) i=++tot;
        tree[i].siz=tree[last].siz+flag;
        tree[i].l=tree[last].l;tree[i].r=tree[last].r;
        if (l==r) return;
        mid=(l+r)/2;
        if (x<=mid) ins(tree[last].l,tree[i].l,l,mid,x,flag);
        else ins(tree[last].r,tree[i].r,mid+1,r,x,flag);
    }
    void bit_ins(int i,int x,int flag)
    {
        for (;i<=n;i+=lowbit(i)) ins(root[i],root[i],1,n,x,flag);
    }
    long long ask(int l,int r,int x,int flag)
    {
        long long ss=0;
        int mid,i;
        if (l==r) return 0;
        mid=(l+r)/2;
        if (flag==0)
        {
            ss=0;
            for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].l].siz;
            for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].l].siz;
            if (x<=mid) 
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l;
               return ask(l,mid,x,flag);
            }
            else 
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r;
               return ss+ask(mid+1,r,x,flag);
            }
        }
        else
        {
            ss=0;
            for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].r].siz;
            for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].r].siz;
            if (x<=mid)
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l;
               return ss+ask(l,mid,x,flag);
            }
            else
            {
               for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r;
               for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r;
               return ask(mid+1,r,x,flag);
            }
        }
    }
    long long bit_ask(int l,int r,int x,int flag)
    {
        q1[0]=q2[0]=0;
        for (;l;l-=lowbit(l)) q1[++q1[0]]=root[l];
        for (;r;r-=lowbit(r)) q2[++q2[0]]=root[r];
        return ask(1,n,x,flag);
    }
    int main()
    {
        freopen("inverse.in","r",stdin);
        freopen("inverse.out","w",stdout);
        
        int m,i,j,k;
        long long ans=0;
        scanf("%d%d",&n,&m);
        for (i=1;i<=n;++i) 
        {
           scanf("%d",&a[i]);
           num[a[i]]=i;
        }
        for (i=m;i>=1;--i)
        {
            scanf("%d",&del[i]);
            a[num[del[i]]]=0;
        }
        build(root[0],1,n);
        for (i=1;i<=n;++i)
            if (a[i])
            {
               a2[++a2[0]]=a[i];
               bit_ins(i,a[i],1);
            }
        ans=work(1,a2[0]);
        for (i=1;i<=m;++i)
        {
            k=del[i];j=num[k];a[j]=k;
            bit_ins(j,k,1);
            if (j>1) ans+=bit_ask(0,j-1,k,1);
            if (j<n) ans+=bit_ask(j,n,k,0);
            ansi[i]=ans;
        }
        for (i=m;i>=1;--i) printf("%lld
    ",ansi[i]);
        
        fclose(stdin);
        fclose(stdout);
    }
    AC code

    bzoj2588 Count on a tree

    题目大意:求树上某链上的第k小。

    思路:dfs序(入+1、出-1),建立静态主席树,查询。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 5000005
    #define M 200005
    #define up 20
    using namespace std;
    struct use{int ls,rs,v;}tr[N];
    int ti[M][2]={0},tot,point[M]={0},next[M],en[M],va[M],bi[M],ki[M][2],root[M]={0},tt=0,
        fa[M][up]={0},dep[M];
    void add(int u,int v){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;
    }
    void dfs(int u,int ff,int de){
        int i,j,v;ti[u][0]=++tot;
        dep[u]=de;fa[u][0]=ff;
        for (i=1;i<up;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
        for (i=point[u];i;i=next[i]){
            if ((v=en[i])==ff) continue;
            dfs(v,u,de+1);
        }ti[u][1]=++tot;
    }
    int lca(int u,int v){
        int i,j;if (dep[u]<dep[v]) swap(u,v);
        for (i=up-1;i>=0;--i)
          if (dep[fa[u][i]]>=dep[v]) u=fa[u][i];
        if (u==v) return u;
        for (i=up-1;i>=0;--i)
          if (fa[u][i]!=fa[v][i]){
              u=fa[u][i];v=fa[v][i];
          }return fa[u][0];
    }
    void ins(int &i,int la,int l,int r,int x,int y){
        tr[i=++tt]=tr[la];tr[i].v+=y;
        if (l==r) return;int mid=l+r>>1;
        if (x<=mid) ins(tr[i].ls=0,tr[la].ls,l,mid,x,y);
        else ins(tr[i].rs=0,tr[la].rs,mid+1,r,x,y);
    }
    int ask(int a,int b,int c,int d,int l,int r,int k){
        if (l==r) return l; int mid=l+r>>1;
        int cc=tr[tr[a].ls].v+tr[tr[b].ls].v-tr[tr[c].ls].v-tr[tr[d].ls].v;
        if (cc<k) return ask(tr[a].rs,tr[b].rs,tr[c].rs,tr[d].rs,mid+1,r,k-cc);
        else return ask(tr[a].ls,tr[b].ls,tr[c].ls,tr[d].ls,l,mid,k);
    }
    int main(){
        int n,m,i,j,u,v,siz,la=0,k;scanf("%d%d",&n,&m);
        for (i=1;i<=n;++i){scanf("%d",&va[i]);bi[i]=va[i];}
        sort(bi+1,bi+n+1);siz=unique(bi+1,bi+n+1)-bi-1;
        for (i=1;i<=n;++i) va[i]=upper_bound(bi+1,bi+siz+1,va[i])-bi-1;
        for (tot=0,i=1;i<n;++i){scanf("%d%d",&u,&v);add(u,v);}
        dfs(1,tot=0,1);
        for (i=1;i<=n;++i){
            ki[ti[i][0]][0]=i;ki[ti[i][0]][1]=1;
            ki[ti[i][1]][0]=i;ki[ti[i][1]][1]=-1;
        }for (i=1;i<=tot;++i) ins(root[i],root[i-1],1,siz,va[ki[i][0]],ki[i][1]);
        for (i=1;i<=m;++i){
            scanf("%d%d%d",&u,&v,&k);u^=la;j=lca(u,v);
            printf("%d",la=bi[ask(root[ti[u][0]],root[ti[v][0]],root[ti[j][0]],
                    root[ti[j][0]-1],1,siz,k)]);
            if (i<m) printf("
    ");
        }
    }
    View Code

    bzoj3551 Peaks加强版

    题目大意:给定一个无向图,q组询问,求从某点出发经过边权不超过v能到的点中,权值第k大的权值。

    思路:肯定从最小生成树上找边。如果是离线的话,启发式合并。因为强制在线,所以可以考虑将并查集合并的时候在一个块的点能一起查询。所以可以在合并的时候,fa[r1]=r2的同时,给r2向r1连边,这样的树形结构保证了边权从上到下递减,dfs序后,能走的区间在一起,可以从一个点向上倍增到不能走,然后从这个点的儿子中二分出能走的区间(这些区间的dfs序是连续的,因为并查集的时候边权是有序的),在静态主席树上查询就可以了。

    (听说还有一种叫做kruskal重构树的东西,就是在x、y合并的时候,新建一个节点t作为x、y的父亲,这样的树是二叉树,叶子节点是原来的点,非叶子节点的点权表示的是一些边权,两个叶子节点lca的权值就是两个点在原树路径上的最大路径权值。)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define M 500005
    #define up 20
    #define nn 2000005
    #define inf 2100000000
    using namespace std;
    struct use{
        int u,v,va;
        bool operator<(const use&a)const{return va<a.va;}
    }ed[M];
    struct tree{int l,r,sz;}tr[nn]={0};
    int fa[N][up],gi[N],fa1[N],root[N]={0},hi[N],bi[N],tt=0,dl[N],dr[N],dui[N],
        point[N]={0},next[N],en[N],va[N],tot=0,zh[N],zl[N],zr[N],zt=0,bz,po1[N]={0},ne1[N];
    int find(int x){
        if (fa1[x]!=x) fa1[x]=find(fa1[x]);
        return fa1[x];}
    void add(int u,int v,int vv){next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=vv;}
    int cmp(int x,int y){return gi[x]<gi[y];}
    void dfs(int u,int ff){
        int i,v;fa[u][0]=ff;
        if (!ff) gi[u]=inf;
        dl[u]=++tt;dui[tt]=u;
        for (i=1;i<up;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
        for (i=point[u];i;i=next[i]){
            ne1[i]=po1[u];po1[u]=i;
        }zl[u]=zt+1;
        for (i=po1[u];i;i=ne1[i]){
            if ((v=en[i])==ff) continue;
            zh[++zt]=v;
        }zr[u]=zt;
        for (i=po1[u];i;i=ne1[i]){
            if ((v=en[i])==ff) continue;
            gi[v]=va[i];dfs(v,u);
        }dr[u]=tt;
    }
    void ins(int la,int &i,int l,int r,int x){
        tr[i=++tt]=tr[la];++tr[i].sz;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x);
        else ins(tr[la].r,tr[i].r,mid+1,r,x);
    }
    int ask(int li,int ri,int l,int r,int x){
        if (l==r) return l;
        int ls=tr[tr[ri].l].sz-tr[tr[li].l].sz;
        int mid=(l+r)>>1;
        if (ls>=x) return ask(tr[li].l,tr[ri].l,l,mid,x);
        else return ask(tr[li].r,tr[ri].r,mid+1,r,x-ls);
    }
    int work(int x,int v,int k){
        int rt,i,l,r,mid,ll,rr;rt=x;
        for (i=up-1;i>=0;--i)
          if (gi[fa[rt][i]]<=v) rt=fa[rt][i];
        if (gi[rt]<=v) rt=fa[rt][0];
        l=zl[rt];r=zr[rt];
        if (l<=r){
          rr=0;
          while(l<=r){
            mid=(l+r)>>1;
            if (gi[zh[mid]]<=v){l=mid+1;rr=mid;}
            else r=mid-1;
          }ll=dl[rt];
          if (rr) rr=dr[zh[rr]];
          else rr=dl[rt];
        }else{ll=rr=dl[rt];}
        k=rr-ll+1-k+1;
        if (k<=0||k>rr-ll+1) return 0;
        else return ask(root[ll-1],root[rr],1,bz,k);
    }
    int main(){
        int n,m,q,i,la=0,r1,r2,x,v,k;
        scanf("%d%d%d",&n,&m,&q);
        for (i=1;i<=n;++i){
            scanf("%d",&hi[i]);
            bi[i]=hi[i];fa1[i]=i;
        }sort(bi+1,bi+n+1);bz=unique(bi+1,bi+n+1)-bi-1;
        for (i=1;i<=n;++i) hi[i]=upper_bound(bi+1,bi+bz+1,hi[i])-bi-1;
        for (i=1;i<=m;++i)
            scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].va);
        sort(ed+1,ed+m+1);
        for (i=1;i<=m;++i){
            r1=find(ed[i].u);r2=find(ed[i].v);
            if (r1!=r2){fa1[r1]=r2;add(r2,r1,ed[i].va);}
        }for (i=1;i<=n;++i){
            r1=find(i);
            if (!dl[r1]) dfs(r1,0);
        }for (tt=0,i=1;i<=n;++i) ins(root[i-1],root[i],1,bz,hi[dui[i]]);
        gi[0]=inf;bi[0]=-1;
        for (i=1;i<=q;++i){
            scanf("%d%d%d",&x,&v,&k);
            if (la!=-1){x^=la;v^=la;k^=la;}
            la=work(x,v,k);
            printf("%d
    ",bi[la]);
            la=bi[la];
        }
    }
    View Code

    bzoj4299||bzoj4408

    题目大意:对于一个可重集,定义它的fs为最小的可重集子集和不能表示的数。给定一个数组ai,求[l,r]这个集合的fs。

    思路:对于一个有序的可重集,如果前i个数能表示到mx,新来一个数x,如果x<=mx+1,则能表示到mx+x。所以我们可以对序列建立主席树,每次查询区间内到mx+1的和,如果这个和<=mx就说明答案是mx+1,否则mx=sm。这个过程每次最少扩大两倍,所以查询的次数是logn的,总复杂度O(nlog^2n)。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define M 4000000
    #define inf 1000000010
    using namespace std;
    struct use{int l,r,sm;}tr[M];
    int rt[N]={0},tt=0;
    void ins(int la,int &i,int l,int r,int x){
        tr[i=++tt]=tr[la];tr[i].sm+=x;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x);
        else ins(tr[la].r,tr[i].r,mid+1,r,x);}
    int ask(int i,int j,int l,int r,int ll,int rr){
        if (!(tr[j].sm-tr[i].sm)) return 0;
        if (ll<=l&&r<=rr) return tr[j].sm-tr[i].sm;
        int mid=(l+r)>>1;int sum=0;
        if (ll<=mid) sum+=ask(tr[i].l,tr[j].l,l,mid,ll,rr);
        if (rr>mid) sum+=ask(tr[i].r,tr[j].r,mid+1,r,ll,rr);
        return sum;}
    int main(){
        int n,i,j,l,r,m,ans,sm;scanf("%d",&n);
        memset(tr,0,sizeof(tr));
        for (i=1;i<=n;++i){
            scanf("%d",&j);ins(rt[i-1],rt[i],1,inf,j);
        }scanf("%d",&m);
        for (i=1;i<=m;++i){
            scanf("%d%d",&l,&r);
            for(ans=0;;){
                sm=ask(rt[l-1],rt[r],1,inf,1,ans+1);
                if (sm<=ans) break;
                ans=sm;
            }printf("%d
    ",ans+1);
        }
    }
    View Code

    bzoj3207 花神的嘲讽计划

    题目大意:给定一个数列ai,m个询问,问ai中x~y有没有长度为k的给定串。

    思路:因为所有m的k一样,所以可以对每个位置和之前共k为的hash值用主席树记录,查询的时候查相应区间有没有hash值。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define M 5000005
    #define ULL unsigned long long
    #define p 467LL
    using namespace std;
    struct use{int l,r,sz;}tr[M];
    int rt[N],ai[N]={0},tt=0;
    ULL bi[N]={0},ci[N],mi[N];
    void ins(int la,int &i,int l,int r,int x){
        tr[i=++tt]=tr[la];++tr[i].sz;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x);
        else ins(tr[la].r,tr[i].r,mid+1,r,x);
    }
    int ask(int i,int j,int l,int r,int x){
        if (!(tr[j].sz-tr[i].sz)) return 0;
        if (l==r) return tr[j].sz-tr[i].sz;
        int mid=(l+r)>>1;
        if (x<=mid) return ask(tr[i].l,tr[j].l,l,mid,x);
        else return ask(tr[i].r,tr[j].r,mid+1,r,x);
    }
    int main(){
        int n,m,k,i,j,x,y,xx,bz;ULL cc;
        memset(tr,0,sizeof(tr));
        scanf("%d%d%d",&n,&m,&k);
        for (mi[0]=1LL,i=1;i<=k;++i) mi[i]=mi[i-1]*p;
        for (bi[0]=0LL,i=1;i<=n;++i){
            scanf("%d",&ai[i]);
            if (i>=k){
                for (j=0;j<k;++j) bi[i]+=(ULL)ai[i-k+1+j]*mi[j];
                ci[i]=bi[i];
            }
        }sort(bi+1,bi+n+1);bz=unique(bi+1,bi+n+1)-bi-1;
        for (i=k;i<=n;++i){
            ai[i]=upper_bound(bi+1,bi+bz+1,ci[i])-bi-1;
            ins(rt[i-1],rt[i],1,bz,ai[i]);
        }for (i=1;i<=m;++i){
            scanf("%d%d",&x,&y);
            for (cc=0LL,j=0;j<k;++j){
                scanf("%d",&xx);
                cc+=(ULL)xx*mi[j];
            }if (y-x+1<k){printf("Yes
    ");continue;}
            j=upper_bound(bi+1,bi+bz+1,cc)-bi-1;
            if (bi[j]!=cc){printf("Yes
    ");continue;}
            if (ask(rt[x+k-2],rt[y],1,bz,j)) printf("No
    ");
            else printf("Yes
    ");
        }
    }
    View Code

    bzoj1926 粟粟的书架

    题目大意:给定一个r*c的矩阵,m个询问,每次询问一个矩阵内最少几个数的和>=h。

    思路:数据分为两部分:1)r,c<=200,m<=200000。考虑建二维主席树,如果四个区间的话儿子信息比较混乱,所以可以暴力插最后一列或一行(可以选较小的那个插),查询四个区间,查询的时候尽量选右区间;2)r=1,c<=500000,m<=20000。直接主席树。

    1)也可以用二分做。二分最小数+二分选最小数的个数。(区间里的数<=1000)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 15000005
    #define M 1000005
    #define nn 205
    using namespace std;
    struct use{int l,r,sz,sm;}tr[N]={0};
    int r1[nn][nn]={0},r2[M]={0},tt=0,bz=0,p1[nn][nn],p2[M],bi[M],s1[nn][nn]={0},s2[M]={0};
    void ins(int la,int &i,int l,int r,int x){
        if (i==la||!i) tr[i=++tt]=tr[la];
        ++tr[i].sz;tr[i].sm+=bi[x];
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x);
        else ins(tr[la].r,tr[i].r,mid+1,r,x);}
    int ask1(int a,int b,int c,int d,int l,int r,int x){
        if (l==r) return (x+bi[l]-1)/bi[l];
        int mid=(l+r)>>1;int cc=tr[tr[a].r].sm-tr[tr[b].r].sm-tr[tr[c].r].sm+tr[tr[d].r].sm;
        if (cc>=x) return ask1(tr[a].r,tr[b].r,tr[c].r,tr[d].r,mid+1,r,x);
        else return tr[tr[a].r].sz-tr[tr[b].r].sz-tr[tr[c].r].sz+tr[tr[d].r].sz+
            ask1(tr[a].l,tr[b].l,tr[c].l,tr[d].l,l,mid,x-cc);}
    int ask2(int i,int j,int l,int r,int x){
        if (l==r) return (x+bi[l]-1)/bi[l];
        int mid=(l+r)>>1;int cc=tr[tr[j].r].sm-tr[tr[i].r].sm;
        if (cc>=x) return ask2(tr[i].r,tr[j].r,mid+1,r,x);
        else return tr[tr[j].r].sz-tr[tr[i].r].sz+ask2(tr[i].l,tr[j].l,l,mid,x-cc);}
    int main(){
        int r,c,m,i,j,k,x1,x2,y1,y2,hi;
        scanf("%d%d%d",&r,&c,&m);
        if (r!=1){
            for (i=1;i<=r;++i)
                for (j=1;j<=c;++j){
                      scanf("%d",&p1[i][j]);bi[++bz]=p1[i][j];
                      s1[i][j]=p1[i][j]+s1[i-1][j]+s1[i][j-1]-s1[i-1][j-1];
                }sort(bi+1,bi+bz+1);
            bz=unique(bi+1,bi+bz+1)-bi-1;
            for (i=1;i<=r;++i)
                for (j=1;j<=c;++j){
                    p1[i][j]=upper_bound(bi+1,bi+bz+1,p1[i][j])-bi-1;
                    if (i<j){
                        r1[i][j]=r1[i][j-1];
                        for (k=1;k<=i;++k)
                            ins(r1[i][j-1],r1[i][j],1,bz,p1[k][j]);
                    }else{
                        r1[i][j]=r1[i-1][j];
                        for (k=1;k<=j;++k)
                            ins(r1[i-1][j],r1[i][j],1,bz,p1[i][k]);
                    }
                }
            for (i=1;i<=m;++i){
                scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&hi);
                if (s1[x2][y2]-s1[x2][y1-1]-s1[x1-1][y2]+s1[x1-1][y1-1]<hi)
                    printf("Poor QLW
    ");
                else printf("%d
    ",ask1(r1[x1-1][y1-1],r1[x2][y1-1],
                                    r1[x1-1][y2],r1[x2][y2],1,bz,hi));
            }
        }else{
            for (i=1;i<=c;++i){
                scanf("%d",&p2[i]);
                bi[++bz]=p2[i];
                s2[i]=s2[i-1]+p2[i];
            }sort(bi+1,bi+bz+1);
            bz=unique(bi+1,bi+bz+1)-bi-1;
            for (i=1;i<=c;++i){
                p2[i]=upper_bound(bi+1,bi+bz+1,p2[i])-bi-1;
                ins(r2[i-1],r2[i],1,bz,p2[i]);
            }for (i=1;i<=m;++i){
                scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&hi);
                if (s2[y2]-s2[y1-1]<hi) printf("Poor QLW
    ");
                else printf("%d
    ",ask2(r2[y1-1],r2[y2],1,bz,hi));
            }
        }
    }
    View Code

    bzoj3123 森林

    题目大意:森林中有点权,支持在线操作:(1)查询x到y的路径上第k大;(2)连接x和y。

    思路:连接的时候相当于把以v为根的子树整个作为u的儿子,所以对于v子树外的点是没有影响的,所以可以直接用rt[u]更新rt[v](!!!),dfs着把以v为根的子树更新过去。因为只会给一些点儿子,所以可以更新这些儿子的fa。查询的时候用四段在主席树上查一下。

    注意:求树上链的信息不需要求出dfs序,直接用rt[fa]更新rt[u]就可以了()。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 200005
    #define M 20000000
    #define up 20
    using namespace std;
    struct use{int l,r,sz;}tr[M];
    int ai[N],bi[N],bz,rt[N],fr[N],la[N],fa[N][up],point[N]={0},next[N],en[N],tot=0,n,tt=0,
        dep[N],anc[N],siz[N];
    bool vi[N]={false};
    int in(){
        char ch=getchar();
        while(ch<'A'||ch>'Z') ch=getchar();
        return (ch=='Q');}
    void add(int u,int v){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;}
    void ins(int la,int &i,int l,int r,int x){
        tr[i=++tt]=tr[la];++tr[i].sz;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x);
        else ins(tr[la].r,tr[i].r,mid+1,r,x);}
    int ask(int a,int b,int c,int d,int l,int r,int x){
        if (l==r) return l;
        int mid=(l+r)>>1;int cc=tr[tr[a].l].sz+tr[tr[b].l].sz-tr[tr[c].l].sz-tr[tr[d].l].sz;
        if (cc>=x) return ask(tr[a].l,tr[b].l,tr[c].l,tr[d].l,l,mid,x);
        else return ask(tr[a].r,tr[b].r,tr[c].r,tr[d].r,mid+1,r,x-cc);}
    int lca(int u,int v){
        if (dep[u]<dep[v]) swap(u,v);
        int i;
        for (i=up-1;i>=0;--i)
          if (dep[fa[u][i]]>=dep[v]) u=fa[u][i];
        if (u==v) return u;
        for (i=up-1;i>=0;--i)
          if (fa[u][i]!=fa[v][i]){
              u=fa[u][i];v=fa[v][i];
          }return fa[u][0];}
    void dfs(int u,int ff,int an){
        int i,v;fa[u][0]=ff;vi[u]=true;
        ins(rt[ff],rt[u],1,bz,ai[u]);
        anc[u]=an;++siz[an];dep[u]=dep[ff]+1;
        for (i=1;i<up;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
        for (i=point[u];i;i=next[i])
            if ((v=en[i])!=ff) dfs(v,u,an);}
    int calc(int u,int v,int k){
        int x,l;l=lca(u,v);
        x=ask(rt[u],rt[v],rt[l],rt[fa[l][0]],1,bz,k);
        return bi[x];}
    void merge(int u,int v){
        if (siz[anc[u]]<=siz[anc[v]]) swap(u,v);
        dfs(v,u,anc[u]);}
    int main(){
        int m,i,j,t,u,v,k,op,la=0;scanf("%d",&i);
        scanf("%d%d%d",&n,&m,&t);
        for (i=1;i<=n;++i){
            scanf("%d",&ai[i]);
            bi[i]=ai[i];
        }sort(bi+1,bi+n+1);
        bz=unique(bi+1,bi+n+1)-bi-1;
        for (i=1;i<=n;++i) ai[i]=upper_bound(bi+1,bi+bz+1,ai[i])-bi-1;
        for (i=1;i<=m;++i){scanf("%d%d",&u,&v);add(u,v);}
        for (i=1;i<=n;++i) if (!vi[i]) dfs(i,0,i);
        for (i=1;i<=t;++i){
            op=in();scanf("%d%d",&u,&v);
            u^=la;v^=la;
            if (op){
                scanf("%d",&k);k^=la;
                printf("%d
    ",la=calc(u,v,k));
            }else{merge(u,v);add(u,v);}
        }
    }
    View Code

    bzoj4504 K个串

    题目大意:给出一个序列,问这个序列子序列和第k大是多少(子序列中一种数字只算一遍)。

    思路:类似超级钢琴,想到要用堆维护以某个点结尾的子序列最大,同时要记录这个最大值取的范围和位置,为了求这些,建立主席树,每种数字只有离这个点最近的那个位置有数,每次查询相当于询问以的区间的右边连续最大和(没有数的位置相当于0,所以如果不存在一个区间,相当于这个区间都是0,所以最大值的位置可以取区间内任意一个位置)。

    注意:每次查询一个区间的右端连续最大和,还要加上这个区间右端点到这个子序列结尾的和才是这一段的答案。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define N 100005
    #define M 4000000
    #define LL long long
    using namespace std;
    struct use{LL rm,sm;int l,r,mp;}tr[M];
    struct uu{
        int rd,l,r,mp;LL mx;
        bool operator<(const uu&x)const{return mx<x.mx;}
    };
    priority_queue<uu> st;
    int rt[N],tt=0,ai[N],ci[N],cz=0,la[N]={0};
    int in(){
        char ch=getchar();int x=0,f=1;
        while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if (ch=='-'){f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x*f;}
    use updata(use x,use y,int r){
        use c;c.sm=x.sm+y.sm;
        c.rm=y.rm;c.mp=y.mp;
        if (y.sm+x.rm>c.rm||!y.mp){
            c.rm=y.sm+x.rm;c.mp=x.mp;
        }if (!c.mp) c.mp=r;
        return c;}
    void ins(int &i,int la,int l,int r,int x,int y){
        tr[i=++tt]=tr[la];
        if (l==r){
            tr[i].sm+=(LL)(y*ai[x]);
            tr[i].rm=tr[i].sm;
            tr[i].mp=l;
            return;
        }int ll,rr,mid=(l+r)>>1;
        if (x<=mid) ins(tr[i].l,tr[la].l,l,mid,x,y);
        else ins(tr[i].r,tr[la].r,mid+1,r,x,y);
        ll=tr[i].l;rr=tr[i].r;
        tr[i]=updata(tr[tr[i].l],tr[tr[i].r],r);
        tr[i].l=ll;tr[i].r=rr;}
    use ask(int i,int l,int r,int ll,int rr){
        if (!i) return tr[i];
        if (ll<=l&&r<=rr) return tr[i];
        int mid=(l+r)>>1;use x1,x2;
        bool f1,f2;f1=f2=false;
        if (ll<=mid){f1=true;x1=ask(tr[i].l,l,mid,ll,rr);}
        if (rr>mid){f2=true;x2=ask(tr[i].r,mid+1,r,ll,rr);}
        if (!f2) return x1;
        if (!f1) return x2;
        return updata(x1,x2,rr);}
    LL asum(int i,int l,int r,int ll,int rr){
        if (!i) return 0LL;
        if (ll<=l&&r<=rr) return tr[i].sm;
        int mid=(l+r)>>1;LL sm=0LL;
        if (ll<=mid) sm+=asum(tr[i].l,l,mid,ll,rr);
        if (rr>mid) sm+=asum(tr[i].r,mid+1,r,ll,rr);
        return sm;}
    int main(){
        int n,k,i,x;LL ans;
        use cc;uu dd;
        n=in();k=in();
        for (i=1;i<=n;++i) ci[++cz]=ai[i]=in();
        sort(ci+1,ci+cz+1);
        cz=unique(ci+1,ci+cz+1)-ci-1;
        memset(tr,0,sizeof(tr));
        for (i=1;i<=n;++i){
            x=upper_bound(ci+1,ci+cz+1,ai[i])-ci-1;
            rt[i]=rt[i-1];
            if (la[x]) ins(rt[i],rt[i],1,n,la[x],-1);
            ins(rt[i],rt[i],1,n,i,1);
            la[x]=i;
            cc=ask(rt[i],1,n,1,i);
            if (cc.mp) st.push((uu){i,1,i,cc.mp,cc.rm});
        }while(k--){
            dd=st.top();st.pop();
            ans=dd.mx;
            if (dd.l<dd.mp){
                cc=ask(rt[dd.rd],1,n,dd.l,dd.mp-1);
                cc.rm+=asum(rt[dd.rd],1,n,dd.mp,dd.rd);
                if (cc.mp) st.push((uu){dd.rd,dd.l,dd.mp-1,cc.mp,cc.rm});
            }if (dd.mp<dd.r){
                cc=ask(rt[dd.rd],1,n,dd.mp+1,dd.r);
                if (dd.r<dd.rd) cc.rm+=asum(rt[dd.rd],1,n,dd.r+1,dd.rd);
                if (cc.mp) st.push((uu){dd.rd,dd.mp+1,dd.r,cc.mp,cc.rm});
            }
        }printf("%I64d
    ",ans);
    }
    View Code

    bzoj4523 路由表

    题目大意:认为两个IP码一样是在转成32二进制之后前x位(已知且不同串的不同)一样。操作:1)插入一个新的IP码和x;2)查一个IP码在一段区间[l,r]内匹配改变的次数(从前往后扫,先匹配1~l-1,在l~r中统计改变次数。只有能匹配且x变长时才会改变匹配)

    思路:建trie树,对每个点建主席树。查询的时候找到1~l-1中最长匹配长度mx,对于l~r中的,记下每个匹配长度(>mx)的最小下标,从后往前扫的时候记一个最小下标,如果当前的比最小下标大就不会改变。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<set>
    #define N 20000000
    #define M 1000000
    #define up 32
    using namespace std;
    struct use{int ch[2];}tr[N];
    struct tree{int l,r,mn;}st[N];
    int tt=0,ai[up+1],cnt=0,bi[up+1],rt[N]={0},tz=0;
    char chin(){
        char ch=getchar();
        while(ch<'A'||ch>'Z') ch=getchar();
        return ch;}
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    void pre(int x,int y){for (int i=(y+1)<<3;i>(y<<3);--i){ai[i]=x&1;x>>=1;}}
    void tch(int &i,int la,int l,int r,int x){
        st[i=++tz]=st[la];st[i].mn=min(st[i].mn,x);
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) tch(st[i].l,st[la].l,l,mid,x);
        else tch(st[i].r,st[la].r,mid+1,r,x);}
    int ask(int i,int l,int r,int ll,int rr){
        if (!i) return N;
        if (ll<=l&&r<=rr) return st[i].mn;
        int mn=N;int mid=(l+r)>>1;
        if (ll<=mid) mn=min(mn,ask(st[i].l,l,mid,ll,rr));
        if (rr>mid) mn=min(mn,ask(st[i].r,mid+1,r,ll,rr));
        return mn;}
    void ins(int x){
        int i,u=0,v;
        for (i=1;i<=x;++i){
            v=tr[u].ch[ai[i]];
            if (!v){
                v=tr[u].ch[ai[i]]=++tt;
                tr[tt]=(use){0,0};
            }u=v;
        }tch(rt[u],rt[u],1,M,++cnt);}
    int find(int l,int r){
        int i,u=0,ans=0,mn;bi[0]=0;
        for (i=1;i<=up;++i){
            u=tr[u].ch[ai[i]];
            if (!u) break;
            if ((ans=ask(rt[u],1,M,l,r))<=r) bi[++bi[0]]=ans;
            mn=st[rt[u]].mn;
            if (mn<l) bi[0]=0;
        }for (ans=0,mn=N,i=bi[0];i;--i)
            if (bi[i]<mn){mn=bi[i];++ans;}
        return ans;}
    int main(){
        int m,i,x,l,r;char ch;
        scanf("%d",&m);
        tr[0]=(use){0,0};
        st[0]=(tree){0,0,N};
        while(m--){
            ch=chin();
            for (i=0;i<4;++i){x=in();pre(x,i);}
            if (ch=='A'){x=in();ins(x);}
            else{
                l=in();r=in();
                printf("%d
    ",find(l,r));
            }
        }
    }
    View Code

    bzoj3956 Count

    题目大意:定义好点对为:i<j,i=j-1||(任意i<k<j,ai[k]<ai[i]&&ai[k]<ai[j]),m组询问,问l~r之间的好点对的个数。强制在线。

    思路:考虑好点对的话,最多2n个。所以可以用单调栈扫出j和之前的好点对(能和j组成好点对的至多到ai[i]>=ai[j]的第一个且比ai[j]小的都会被弹掉),用主席树维护。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 300005
    #define M 12000000
    using namespace std;
    struct use{int l,r,sz;}tr[M];
    int rt[N]={0},tt=0,ai[N],zh[N],zt=0;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    void ins(int &i,int la,int l,int r,int x){
        tr[i=++tt]=tr[la];++tr[i].sz;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[i].l,tr[la].l,l,mid,x);
        else ins(tr[i].r,tr[la].r,mid+1,r,x);}
    int ask(int i,int j,int l,int r,int ll,int rr){
        if (!(tr[j].sz-tr[i].sz)) return 0;
        if (ll<=l&&r<=rr) return tr[j].sz-tr[i].sz;
        int sm=0,mid=(l+r)>>1;
        if (ll<=mid) sm+=ask(tr[i].l,tr[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=ask(tr[i].r,tr[j].r,mid+1,r,ll,rr);
        return sm;}
    int main(){
        int n,m,tp,i,j,l,r,la=0;
        memset(tr,0,sizeof(tr));
        n=in();m=in();tp=in();
        for (i=1;i<=n;++i) ai[i]=in();
        for (i=1;i<=n;++i){
            rt[i]=rt[i-1];
            while(zt&&ai[zh[zt]]<ai[i]){
                ins(rt[i],rt[i],1,n,zh[zt]);--zt;
            }if (zt) ins(rt[i],rt[i],1,n,zh[zt]);
            if (zt&&ai[zh[zt]]==ai[i]) --zt;
            zh[++zt]=i;
        }for (i=1;i<=m;++i){
            l=in();r=in();
            if (tp){l=(l+la-1)%n+1;r=(r+la-1)%n+1;}
            if (l>r) swap(l,r);
            la=ask(rt[l-1],rt[r],1,n,l,r);
            printf("%d
    ",la);
        }
    }
    View Code

    bzoj4571 美味

    题目大意:给定一些aj,对于每组bi和xi,求max bi^(aj+xi)。

    思路:按位考虑,用主席树保存权值的话,对于高位确定的数,低位是连续的,类似trie二分的查询,看某一位的时候通过之前选的数、当前位选0/1和xi来确定一个范围,看当前位能否选0/1(!!)。O(nlog^2n)。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 200005
    #define nn 100000
    #define up 19
    #define M 4000005
    using namespace std;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    struct use{int sz,l,r;}tr[M];
    int tt=0,rt[N]={0};
    void ins(int la,int &i,int l,int r,int x){
        tr[i=++tt]=tr[la];++tr[i].sz;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x);
        else ins(tr[la].r,tr[i].r,mid+1,r,x);
    }
    int query(int i,int j,int l,int r,int ll,int rr){
        if (ll>r||rr<l) return 0;
        if (!(tr[j].sz-tr[i].sz)) return 0;
        if (ll<=l&&r<=rr) return tr[j].sz-tr[i].sz;
        int mid=(l+r)>>1;int sm=0;
        if (ll<=mid) sm+=query(tr[i].l,tr[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=query(tr[i].r,tr[j].r,mid+1,r,ll,rr);
        return sm;}
    int ask(int d,int b,int x,int l,int r,int y){
        if (d<0) return 0;
        int a=(b>>d)&1;
        if (query(rt[l-1],rt[r],0,nn,y+((a^1)<<d)-x,y+((a^1)<<d)+(1<<d)-1-x)){
            return ask(d-1,b,x,l,r,y+((a^1)<<d))+(1<<d);
        }else return ask(d-1,b,x,l,r,y+(a<<d));
    }
    int main(){
        int n,m,i,b,x,l,r;n=in();m=in();
        for (i=1;i<=n;++i){
            x=in();
            ins(rt[i-1],rt[i],0,nn,x);
        }for (i=1;i<=m;++i){
            b=in();x=in();l=in();r=in();
            printf("%d
    ",ask(up,b,x,l,r,0));
        }
    }
    View Code

    noi模拟赛 线段树(!!!

    题目大意:给出n个数和m个操作,每个操作是将[l,r]都赋成[l,r]的最大值,q个询问:1)ai=j;2)问进行[L,R]操作后,ak的值。

    思路:进行操作相当于取从后往前能扩展的并集的区间内的最大值。用li、ri表示从i向前取2^j个能相交扩展的操作的标号,求li[i][0]、ri[i][0]的时候,用主席树求一下左右端点处最靠后编号的编号,区间赋值的时候打永久化标记。查询的时候先找到k所在的R操作之前最后一个区间,然后用倍增数组找到左右边界,查询区间最值。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define M 4000005
    #define up 20
    using namespace std;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    struct use{int l,r,v;}tr[M];
    struct qq{int l,r;}bi[N];
    int ai[N],seg[N<<2],n,m,tt=0,rt[N]={0},li[N][up],ri[N][up];
    void build(int i,int l,int r){
        if (l==r){seg[i]=ai[l];return;}
        int mid=(l+r)>>1;
        build(i<<1,l,mid);build(i<<1|1,mid+1,r);
        seg[i]=max(seg[i<<1],seg[i<<1|1]);
    }
    void sch(int i,int l,int r,int x,int y){
        if (l==r){seg[i]=y;return;}
        int mid=(l+r)>>1;
        if (x<=mid) sch(i<<1,l,mid,x,y);
        else sch(i<<1|1,mid+1,r,x,y);
        seg[i]=max(seg[i<<1],seg[i<<1|1]);
    }
    int smx(int i,int l,int r,int ll,int rr){
        if (ll<=l&&r<=rr) return seg[i];
        int mx=0,mid=(l+r)>>1;
        if (ll<=mid) mx=max(mx,smx(i<<1,l,mid,ll,rr));
        if (rr>mid) mx=max(mx,smx(i<<1|1,mid+1,r,ll,rr));
        return mx;
    }
    int ask(int i,int l,int r,int x){
        int mx=tr[i].v;
        if (l==r) return mx;
        int mid=(l+r)>>1;
        if (x<=mid) mx=max(mx,ask(tr[i].l,l,mid,x));
        else mx=max(mx,ask(tr[i].r,mid+1,r,x));
        return mx;}
    void tch(int &i,int j,int l,int r,int ll,int rr,int x){
        tr[i=++tt]=tr[j];
        if (ll<=l&&r<=rr){tr[i].v=x;return;}
        int mid=(l+r)>>1;
        if (ll<=mid) tch(tr[i].l,tr[j].l,l,mid,ll,rr,x);
        if (rr>mid) tch(tr[i].r,tr[j].r,mid+1,r,ll,rr,x);
    }
    int main(){
        freopen("segment.in","r",stdin);
        freopen("segment.out","w",stdout);
        
        int q,i,j,l,r,ll,rr,k,op;n=in();m=in();q=in();
        for (i=1;i<=n;++i) ai[i]=in();
        build(1,1,n);
        memset(tr,0,sizeof(tr));
        for (i=1;i<=m;++i){
            bi[i]=(qq){l=in(),r=in()};
            li[i][0]=ask(rt[i-1],1,n,l);
            for (j=1;j<up;++j) li[i][j]=li[li[i][j-1]][j-1];
            ri[i][0]=ask(rt[i-1],1,n,r);
            for (j=1;j<up;++j) ri[i][j]=ri[ri[i][j-1]][j-1];
            tch(rt[i],rt[i-1],1,n,l,r,i);
        }while(q--){
            if ((op=in())==1){
                l=in();r=in();ai[l]=r;
                sch(1,1,n,l,r);
            }else{
                l=in();r=in();k=in();
                j=ask(rt[r],1,n,k);
                if (j>=l){
                    for (ll=j,i=up-1;i>=0;--i)
                        if (li[ll][i]>=l) ll=li[ll][i];
                    for (rr=j,i=up-1;i>=0;--i)
                        if (ri[rr][i]>=l) rr=ri[rr][i];
                    ll=bi[ll].l;rr=bi[rr].r;
                }else ll=rr=k;
                printf("%d
    ",smx(1,1,n,ll,rr));
            }
        }
    }
    View Code

    noi模拟赛 基因改造计划(!!!

    题目大意:给出一个长度为n的字符串,m组询问:[l,r]中回文串的个数。

    思路:把相邻字符中加入其它字符之后先用manacher处理处每个位置能延伸的最长长度fi(包含自己这一位),如果中间是字母,个数是fi[i]/2;如果是其他字符,个数是(fi[i]+1)/2,这里+1的原因是这样保证了分母的都是偶数,可以加起来之后再/2。每个询问[l,r]相当于L=2l-1,R=2r+1的区间中(sigma(i=L~R) min(fi[i],i-L+1,R-i+1))/2,分情况讨论:1)当i-L+1<=R-i+1,也就是L<=i<=l+r时,sigma(i=L~l+r) min(fi[i],i-L+1),再分情况,就是i-fi[i]<=L时算k-l,i-fi[i]>L时算fi[i],可以用主席树(第一维编号i,第二维i-fi[i])维护区间的fi[i]、i的和与个数,统计答案;2)当i-L+1>R-i+1,也就是l+r<i<=R时,sigma(i=L~l+r) min(fi[i],R-i+1),分情况的时候根据i+fi[i]和R的关系,统计答案。

    注意:取min的时候,先化式子,然后尽量分情况,固定某些区间取某些值,从而可以用数据结构或其他维护。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 300005
    #define M 6000005
    #define LL long long
    #define mid (l+r)/2
    #define L l*2-1
    #define R r*2+1
    using namespace std;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    char chin(){
        char ch=getchar();
        while(ch<'A'||ch>'Z') ch=getchar();
        return ch;}
    char ss[N];
    struct use{int l,r,c;LL a,b;}tl[M],tr[M];
    int lz=0,rz=0,sz=0,fi[N]={0},lt[N]={0},rt[N]={0};
    void mana(){
        int i,mx=0,id=0;
        for (i=1;i<sz;++i){
            if (mx>i){fi[i]=min(fi[id*2-i],mx-i);}
            else fi[i]=1;
            for (;ss[i+fi[i]]==ss[i-fi[i]];++fi[i]);
            if (i+fi[i]>mx){mx=i+fi[i];id=i;}
        }
    }
    void insl(int &i,int la,int l,int r,int x,int a,int b){
        tl[i=++lz]=tl[la];++tl[i].c;
        tl[i].a+=a;tl[i].b+=b;
        if (l==r) return;
        if (x<=mid) insl(tl[i].l,tl[la].l,l,mid,x,a,b);
        else insl(tl[i].r,tl[la].r,mid+1,r,x,a,b);
    }
    void insr(int &i,int la,int l,int r,int x,int a,int b){
        tr[i=++rz]=tr[la];++tr[i].c;
        tr[i].a+=a;tr[i].b+=b;
        if (l==r) return;
        if (x<=mid) insr(tr[i].l,tr[la].l,l,mid,x,a,b);
        else insr(tr[i].r,tr[la].r,mid+1,r,x,a,b);
    }
    LL ala(int i,int j,int l,int r,int ll,int rr){
        if (!(tl[j].a-tl[i].a)) return 0LL;
        if (ll<=l&&r<=rr) return tl[j].a-tl[i].a;
        LL sm=0LL;
        if (ll<=mid) sm+=ala(tl[i].l,tl[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=ala(tl[i].r,tl[j].r,mid+1,r,ll,rr);
        return sm;}
    LL alb(int i,int j,int l,int r,int ll,int rr){
        if (!(tl[j].b-tl[i].b)) return 0LL;
        if (ll<=l&&r<=rr) return tl[j].b-tl[i].b;
        LL sm=0LL;
        if (ll<=mid) sm+=alb(tl[i].l,tl[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=alb(tl[i].r,tl[j].r,mid+1,r,ll,rr);
        return sm;}
    int alc(int i,int j,int l,int r,int ll,int rr){
        if (!(tl[j].c-tl[i].c)) return 0;
        if (ll<=l&&r<=rr) return tl[j].c-tl[i].c;
        int sm=0;
        if (ll<=mid) sm+=alc(tl[i].l,tl[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=alc(tl[i].r,tl[j].r,mid+1,r,ll,rr);
        return sm;}
    LL ara(int i,int j,int l,int r,int ll,int rr){
        if (!(tr[j].a-tr[i].a)) return 0LL;
        if (ll<=l&&r<=rr) return tr[j].a-tr[i].a;
        LL sm=0LL;
        if (ll<=mid) sm+=ara(tr[i].l,tr[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=ara(tr[i].r,tr[j].r,mid+1,r,ll,rr);
        return sm;}
    LL arb(int i,int j,int l,int r,int ll,int rr){
        if (!(tr[j].b-tr[i].b)) return 0LL;
        if (ll<=l&&r<=rr) return tr[j].b-tr[i].b;
        LL sm=0LL;
        if (ll<=mid) sm+=arb(tr[i].l,tr[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=arb(tr[i].r,tr[j].r,mid+1,r,ll,rr);
        return sm;}
    int arc(int i,int j,int l,int r,int ll,int rr){
        if (!(tr[j].c-tr[i].c)) return 0;
        if (ll<=l&&r<=rr) return tr[j].c-tr[i].c;
        int sm=0;
        if (ll<=mid) sm+=arc(tr[i].l,tr[j].l,l,mid,ll,rr);
        if (rr>mid) sm+=arc(tr[i].r,tr[j].r,mid+1,r,ll,rr);
        return sm;}
    int main(){
        freopen("gene.in","r",stdin);
        freopen("gene.out","w",stdout);
        
        int n,m,i,l,r;LL ans=0LL;n=in();m=in();
        ss[sz++]='B';ss[sz++]='#';
        for (i=1;i<=n;++i){
            ss[sz++]=chin();ss[sz++]='#';
        }ss[sz]='B';mana();
        for (i=1;i<sz;++i){
            insl(lt[i],lt[i-1],0,sz,i-fi[i],i,fi[i]);
            insr(rt[i],rt[i-1],0,sz,i+fi[i],i,fi[i]);
        }while(m--){
            l=in();r=in();
            ans=ala(lt[L-1],lt[l+r],0,sz,0,L)-(LL)alc(lt[L-1],lt[l+r],0,sz,0,L)*(L-1)+
                (L<sz ? alb(lt[L-1],lt[l+r],0,sz,L+1,sz) : 0LL);
            if (l+r<R) ans+=arb(rt[l+r],rt[R],0,sz,0,R)+(R<sz ? 
                    (LL)arc(rt[l+r],rt[R],0,sz,R+1,sz)*(R+1)-
                        ara(rt[l+r],rt[R],0,sz,R+1,sz) : 0LL);
            ans-=(LL)(r-l+2);
            printf("%I64d
    ",ans/2LL);
        }
    }
    View Code

    其他树套树问题

    cogs1345 K大数查询

    题目大意:有n个连续的区间,每个区间可以放多个数,区间修改,查找区间内的第K大。

    思路:树状数组(权值)套线段树(位置)。因为不会写其他的树套树,也不会树状数组的区间修改,于是只能写成这样了。插入的时候log^2n,查询的时候二分一下log^3n,然后就会发现自己T了。因为自己用了pushdown,然后就会开很多没有用的点,也会一遍一遍的访问他们。那我们只能不用pushdown,然后传入一个变量fadel,然后更新一下就可以了!!!那么这里的siz保存的就是这个节点及它下面的元素个数,并没有保存它上方的!!!因为这里是第K大,而我只会树状数组第K小,所以又用了一棵线段树来维护区间元素的个数,以转化成第K小。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    struct use{
        int l,r,siz,del;
    }tree[20000000]={0};
    int tot=0,wotr[200000]={0},delta[200000]={0},root[50001]={0},n,size=0;
    int lowbit(int x){return x&(-x);}
    void paint(int i,int x,int l,int r)
    {
        delta[i]+=x;wotr[i]+=x*(r-l+1);
    }
    void pushdown(int i,int l,int r)
    {
        int mid;
        mid=(l+r)/2;
        paint(i*2,delta[i],l,mid);
        paint(i*2+1,delta[i],mid+1,r);
        delta[i]=0;
    }
    void insert(int i,int l,int r,int ll,int rr)
    {
        int mid;
        if (ll<=l&&r<=rr)
        {
            paint(i,1,l,r);
            return;
        }
        mid=(l+r)/2;
        pushdown(i,l,r);
        if  (ll<=mid) insert(i*2,l,mid,ll,rr);
        if (rr>mid) insert(i*2+1,mid+1,r,ll,rr);
        wotr[i]=wotr[i*2]+wotr[i*2+1];
    }
    int work(int i,int l,int r,int ll,int rr)
    {
        int mid,sum=0;
        if (ll<=l&&r<=rr) return wotr[i];
        pushdown(i,l,r);mid=(l+r)/2;
        if (ll<=mid) sum+=work(i*2,l,mid,ll,rr);
        if (rr>mid) sum+=work(i*2+1,mid+1,r,ll,rr);
        return sum;
    }
    void ins(int &i,int l,int r,int ll,int rr,int x,int fadel)
    {
        int mid;
        if (i==0) i=++tot;
        if (ll<=l&&r<=rr)
        {
            ++tree[i].del;tree[i].siz=(tree[i].l==0?0:tree[tree[i].l].siz)+(tree[i].r==0?0:tree[tree[i].r].siz)+tree[i].del*(r-l+1);
            return;
        }
        mid=(l+r)/2;
        if (ll<=mid) ins(tree[i].l,l,mid,ll,rr,x,fadel+tree[i].del);
        if (rr>mid) ins(tree[i].r,mid+1,r,ll,rr,x,fadel+tree[i].del);
        tree[i].siz=(tree[i].l==0?0:tree[tree[i].l].siz)+(tree[i].r==0?0:tree[tree[i].r].siz)+tree[i].del*(r-l+1);
    }
    void bit_ins(int x,int l,int r)
    {
        for (;x<=n;x+=lowbit(x)) ins(root[x],1,n,l,r,1,0);
    }
    int ask(int i,int l,int r,int ll,int rr,int fadel)
    {
        int mid,sum=0;
        if (!i) return (fadel*(min(r,rr)-max(l,ll)+1));
        if (ll<=l&&r<=rr) return tree[i].siz+fadel*(r-l+1);
        mid=(l+r)/2;
        if (ll<=mid) sum+=ask(tree[i].l,l,mid,ll,rr,tree[i].del+fadel);
        if (rr>mid) sum+=ask(tree[i].r,mid+1,r,ll,rr,tree[i].del+fadel);
        return sum;
    }
    int bit_ask(int x,int ll,int rr)
    {
        int sum=0;
        for (;x>0;x-=lowbit(x)) sum+=ask(root[x],1,n,ll,rr,0);
        return sum;
    }
    int to_ask(int l,int r,int ll,int rr,int k)
    {
        int mid,sum=0,ans;
        if (l==r) return l;
        mid=(l+r)/2;
        sum=bit_ask(mid,ll,rr);
        if (k<=sum) ans=to_ask(l,mid,ll,rr,k);
        else ans=to_ask(mid+1,r,ll,rr,k);
        return ans;
    }
    int main()
    {
        freopen("zjoi13_sequence.in","r",stdin);
        freopen("zjoi13_sequence.out","w",stdout);
        
        int m,i,j,kind,aa,bb,cc;
        scanf("%d%d",&n,&m);
        for (i=1;i<=m;++i)
        {
            scanf("%d%d%d%d",&kind,&aa,&bb,&cc);
            if (kind==1)
            {
                bit_ins(cc,aa,bb);
                insert(1,1,n,aa,bb);
            }
            else
            {
                j=work(1,1,n,aa,bb);
                cc=j-cc+1;
                j=to_ask(1,n,aa,bb,cc);
                printf("%d
    ",j);
            }
        }
        
        fclose(stdin);
        fclose(stdout);
    }
    View Code

    bzoj1146 网络管理

    题目大意:树上带修改第k大查询。

    思路:链剖+线段树套平衡树。暴力建树,查询的时候先二分答案,然后链剖求小于等于它的个数。改的时候删点加点就可以了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxnode 100000
    using namespace std;
    struct node{
        node *ch[2];
        int r,v,num,siz;
        node(int v):v(v) {siz=num=1;r=rand();ch[0]=ch[1]=NULL;}
        int cmp(int x){return x==v ? -1 : x>v;}
        void updata()
        {
            siz=num;
            if (ch[0]!=NULL) siz+=ch[0]->siz;
            if (ch[1]!=NULL) siz+=ch[1]->siz;
        }
    }*tree[maxnode*4];
    int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},ti[maxnode]={0},id[maxnode]={0},tot=0,n;
    void add(int u,int v)
    {
        ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;
        ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;
    }
    void rotate(node* &o,int d)
    {
        node *k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;
        o->updata();k->updata();o=k;
    }
    void insert(node* &o,int v)
    {
        if (o==NULL) o=new node(v);
        else
        {
          int d=o->cmp(v);
          if (d==-1) ++o->num;
          else
          {
            insert(o->ch[d],v);
            if (o->ch[d]->r > o->r) rotate(o,d^1);
          }
        }
        o->updata();
    }
    void del(node* &o,int v)
    {
        int d=o->cmp(v);
        if (d==-1)
        {
            if (o->num==1)
            {
                if (o->ch[0]==NULL) o=o->ch[1];
                else
                {
                    if (o->ch[1]==NULL) o=o->ch[0];
                    else
                    {
                        int d2=(o->ch[0]->r > o->ch[1]->r ? 1 : 0);
                        rotate(o,d2);del(o->ch[d2],v);
                    }
                }
            }
            else -- o->num;
        }
        else del(o->ch[d],v);
        if (o!=NULL) o->updata();
    }
    int kth(node *o,int k)
    {
        if ((o->ch[0]==NULL ? 0 : o->ch[0]->siz)>=k) return kth(o->ch[0],k);
        if ((o->ch[0]==NULL ? 0 : o->ch[0]->siz)+ o->num <k) 
          return kth(o->ch[1],k-(o->ch[0]==NULL ? 0 : o->ch[0]->siz)- o->num);
        return o->v;
    }
    int rank(node *o,int k)
    {
        if (o==NULL) return 0;
        if (o->v==k) return (o->ch[0]==NULL ? 0 : o->ch[0]->siz) + o->num;
        if (o->v > k) return rank(o->ch[0],k);
        if (o->v < k) return rank(o->ch[1],k)+o->num+(o->ch[0]==NULL ? 0 : o->ch[0]->siz);
    }
    void inst(node* &o,int l,int r)
    {
        int i;
        for (i=l;i<=r;++i) insert(o,ti[id[i]]);
    }
    void build(int i,int l,int r)
    {
        int mid;
        inst(tree[i],l,r);if (l==r) return;
        mid=(l+r)/2;build(i*2,l,mid);build(i*2+1,mid+1,r);
    }
    void tch(int i,int l,int r,int x,int y)
    {
        int mid;
        if (l==r){tree[i]=new node(y);return;}
        mid=(l+r)/2;
        if (x<=mid) tch(i*2,l,mid,x,y);
        else tch(i*2+1,mid+1,r,x,y);
        del(tree[i],ti[id[x]]);insert(tree[i],y);
    }
    int tsum(int i,int l,int r,int ll,int rr)
    {
        int mid,sum=0;
        if (ll<=l&&r<=rr) return tree[i]->siz;
        mid=(l+r)/2;
        if (ll<=mid) sum+=tsum(i*2,l,mid,ll,rr);
        if (rr>mid) sum+=tsum(i*2+1,mid+1,r,ll,rr);
        return sum;
    }
    int task(int i,int l,int r,int ll,int rr,int x)
    {
        int mid,sum=0;
        if (ll<=l&&r<=rr) return rank(tree[i],x);
        mid=(l+r)/2;
        if (ll<=mid) sum+=task(i*2,l,mid,ll,rr,x);
        if (rr>mid) sum+=task(i*2+1,mid+1,r,ll,rr,x);
        return sum;
    }
    struct lp{
        int fa[maxnode],son[maxnode],dep[maxnode],siz[maxnode],tid[maxnode],top[maxnode];
        bool visit[maxnode];
        void dfs1(int u,int f,int depth)
        {
            int i,j,maxsiz=0;
            son[u]=0;fa[u]=f;dep[u]=depth;siz[u]=1;
            for (i=point[u];i;i=next[i])
                if (en[i]!=f)
                {
                    dfs1(en[i],u,depth+1);siz[u]+=siz[en[i]];
                    if (siz[en[i]]>maxsiz)
                    {
                        son[u]=en[i];maxsiz=siz[en[i]];
                    }
                }
        }
        void dfs2(int u,int anc)
        {
            int i,j;
            tid[u]=++tot;id[tot]=u;
            top[u]=anc;visit[u]=true;
            if (son[u]) dfs2(son[u],anc);
            for (i=point[u];i;i=next[i])
                if (!visit[en[i]]) dfs2(en[i],en[i]);
        }
        int qsum(int a,int b)
        {
            int sum=0;
            while(top[a]!=top[b])
            {
                if (dep[top[a]]<dep[top[b]]) swap(a,b);
                sum+=tsum(1,1,n,tid[top[a]],tid[a]);
                a=fa[top[a]];
            }
            if (dep[a]>dep[b]) swap(a,b);
            sum+=tsum(1,1,n,tid[a],tid[b]); return sum;
        }
        int qask(int a,int b,int k)
        {
            int sum=0;
            while(top[a]!=top[b])
            {
                if (dep[top[a]]<dep[top[b]]) swap(a,b);
                sum+=task(1,1,n,tid[top[a]],tid[a],k);
                a=fa[top[a]];
            }
            if (dep[a]>dep[b]) swap(a,b);
            sum+=task(1,1,n,tid[a],tid[b],k);return sum;
        }
    }t;
    void ask(int a,int b,int k)
    {
        int l,r,mid,sum,i;
        sum=t.qsum(a,b);
        if (k>sum){printf("invalid request!
    ");return;}
        k=sum+1-k;l=1;r=n;
        while(l<r)
        {
            mid=(l+r)/2;
            i=kth(tree[1],mid);
            sum=t.qask(a,b,i);
            if (sum>k-1) r=mid;
            else l=mid+1;
        }
        printf("%d
    ",kth(tree[1],l));
    }
    int main()
    {
        int q,i,j,u,v,k;
        scanf("%d%d",&n,&q);
        for (i=1;i<=n;++i) scanf("%d",&ti[i]);
        for (i=1;i<n;++i){scanf("%d%d",&u,&v);add(u,v);}
        t.dfs1(1,0,1);tot=0;t.dfs2(1,1);build(1,1,n);
        for (i=1;i<=q;++i)
        {
            scanf("%d%d%d",&k,&u,&v);
            if (k==0)
            {
                tch(1,1,n,t.tid[u],v);ti[u]=v;
            }
            else ask(u,v,k);
        }
    }
    View Code

    bzoj3196 二逼平衡树

    题目大意:区间第k小、排名、前驱、后继,单点修改。

    思路:线段树套平衡树。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxnode 50005
    #define inf 2100000000LL
    using namespace std;
    struct node{
        node *ch[2];
        int r,v,num,siz;
        node(int v):v(v) {siz=num=1;r=rand();ch[0]=ch[1]=NULL;}
        int cmp(int x){return x==v ? -1 : x>v;}
        int lsiz(){return (ch[0]==NULL ? 0 : ch[0]->siz);}
        int rsiz(){return (ch[1]==NULL ? 0 : ch[1]->siz);}
        void updata(){siz=num+lsiz()+rsiz();}
    }*tree[maxnode*4];
    int ai[maxnode]={0},n,aa;
    inline void rotate(node* &o,int d)
    {
        node *k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;
        o->updata();k->updata();o=k;
    }
    inline void insert(node* &o,int v)
    {
        if (o==NULL) o=new node(v);
        else
        {
          int d=o->cmp(v);
          if (d==-1) ++o->num;
          else
          {
            insert(o->ch[d],v);
            if (o->ch[d]->r > o->r) rotate(o,d^1);
          }
        }
        o->updata();
    }
    inline void del(node* &o,int v)
    {
        int d=o->cmp(v);
        if (d==-1)
        {
            if (o->num==1)
            {
                if (o->ch[0]==NULL) o=o->ch[1];
                else
                {
                    if (o->ch[1]==NULL) o=o->ch[0];
                    else
                    {
                        int d2=(o->ch[0]->r > o->ch[1]->r ? 1 : 0);
                        rotate(o,d2);del(o->ch[d2],v);
                    }
                }
            }
            else -- o->num;
        }
        else del(o->ch[d],v);
        if (o!=NULL) o->updata();
    }
    inline int rank(node* &o,int x)
    {
        if (o==NULL) return 0;
        int d=o->cmp(x);
        if (d==-1) return o->lsiz()+o->num;
        if (d==0) return rank(o->ch[d],x);
        else return rank(o->ch[d],x) + o->num + o->lsiz();
    }
    inline int kth(node* &o,int x)
    {
        if (o->lsiz() >= x) return kth(o->ch[0],x);
        if (o->lsiz() + o->num >=x) return o->v;
        else return kth(o->ch[1],x- o->lsiz() - o->num);
    }
    inline void pre(node* &o,int x)
    {
        if (o==NULL) return;
        if (o->v < x){aa=max(aa,o->v);pre(o->ch[1],x);}
        else pre(o->ch[0],x);
    }
    inline void succ(node* &o,int x)
    {
        if (o==NULL) return;
        if (o->v > x){aa=min(aa,o->v);succ(o->ch[0],x);}
        else succ(o->ch[1],x);
    }
    inline void inst(node* &o,int l,int r)
    {
        for (int i=l;i<=r;++i) insert(o,ai[i]);
    }
    inline void build(int i,int l,int r)
    {
        int mid;
        inst(tree[i],l,r);if (l==r) return;
        mid=(l+r)/2;build(i*2,l,mid);build(i*2+1,mid+1,r);
    }
    inline int qsum(int i,int l,int r,int ll,int rr)
    {
        if (ll<=l&&r<=rr) return tree[i]->siz;
        int mid=(l+r)/2;int sum=0;
        if (ll<=mid) sum+=qsum(i*2,l,mid,ll,rr);
        if (rr>mid) sum+=qsum(i*2+1,mid+1,r,ll,rr);
        return sum;
    }
    inline void tch(int i,int l,int r,int x,int y)
    {
        if (l==r){tree[i]=new node(y);return;}
        int mid=(l+r)/2;
        if (x<=mid) tch(i*2,l,mid,x,y);
        else tch(i*2+1,mid+1,r,x,y);
        del(tree[i],ai[x]);insert(tree[i],y);
    }
    inline int qrank(int i,int l,int r,int ll,int rr,int k)
    {
        if (ll<=l&&r<=rr) return rank(tree[i],k);
        int mid=(l+r)/2;int sum=0;
        if (ll<=mid) sum+=qrank(i*2,l,mid,ll,rr,k);
        if (rr>mid) sum+=qrank(i*2+1,mid+1,r,ll,rr,k);
        return sum;
    }
    inline int qkth(int ll,int rr,int k)
    {
        int sum,l,r,mid,i;
        l=1;r=n;sum=rr-ll+1;
        while(l<r)
        {
            mid=(l+r)/2;i=kth(tree[1],mid);
            sum=qrank(1,1,n,ll,rr,i);
            if (sum>=k) r=mid;
            else l=mid+1;
        } return kth(tree[1],l);
    }
    inline int qpre(int i,int l,int r,int ll,int rr,int x)
    {
        if (ll<=l&&r<=rr){aa=-inf;pre(tree[i],x);return aa;}
        int mid=(l+r)/2;int ans=-inf;
        if (ll<=mid) ans=max(ans,qpre(i*2,l,mid,ll,rr,x));
        if (rr>mid) ans=max(ans,qpre(i*2+1,mid+1,r,ll,rr,x));
        return ans;
    }
    inline int qsucc(int i,int l,int r,int ll,int rr,int x)
    {
        if (ll<=l&&r<=rr){aa=inf;succ(tree[i],x);return aa;}
        int mid=(l+r)/2;int ans=inf;
        if (ll<=mid) ans=min(ans,qsucc(i*2,l,mid,ll,rr,x));
        if (rr>mid) ans=min(ans,qsucc(i*2+1,mid+1,r,ll,rr,x));
        return ans;
    }
    int main()
    {
        freopen("psh.in","r",stdin);
        freopen("psh.out","w",stdout);
        
        int m,i,j,l,r,k,opt;
        scanf("%d%d",&n,&m);
        for (i=1;i<=n;++i) scanf("%d",&ai[i]);
        build(1,1,n);
        for (i=1;i<=m;++i)
        {
            scanf("%d",&opt);
            if (opt!=3)
            {
                scanf("%d%d%d",&l,&r,&k);if (l>r) swap(l,r);
                if (opt==1) printf("%d
    ",qrank(1,1,n,l,r,k-1)+1);
                if (opt==2) printf("%d
    ",qkth(l,r,k));
                if (opt==4) printf("%d
    ",qpre(1,1,n,l,r,k));
                if (opt==5) printf("%d
    ",qsucc(1,1,n,l,r,k));
            }
            else{scanf("%d%d",&l,&k);tch(1,1,n,l,k);ai[l]=k;}
        }
        
        fclose(stdin);
        fclose(stdout);
    }
    View Code

    bzoj2653 middle

    题目大意:给定一个数列,求左端点在[a,b]、右端点在[c,d]的子序列中位数的最大值(如果n个数从0开始编号,中位数是排序后的第n/2位数)。强制在线。

    思路:二分+权值线段树套位置线段树(!!!)。二分答案x,>=x的赋成-1,<=x的赋成1,相当于求左端点[a,b]右端点[c,d]是否存在一段区间和<=0,[b,c]的情况是固定的,所以要求[a,b-1]和[c+1,d]的左或右延续最小子段和,用树套树维护。

    注意:树套树中有一些节点是空的,这些节点所维护的信息在每一次updata前都要注意更新到。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 20005
    #define M 1000000
    using namespace std;
    struct uu{
        int x,po;
        bool operator<(const uu&a)const{return x<a.x;}
    }ni[N];
    struct use{int l,r,sz,ls,rs,len;}tr[M]={0};
    int ai[N],bi[N],zh[4],bz,rt[N]={0},tt=0,n;
    use updata(use cc,use x,use y){
        use c;c.l=cc.l;c.r=cc.r;
        c.sz=x.sz+y.sz;
        c.len=max(cc.len,x.len+y.len);
        c.ls=min(x.ls,x.sz-(x.len-x.sz)+y.ls);
        c.rs=min(y.rs,y.sz-(y.len-y.sz)+x.rs);
        return c;}
    void ins(int la,int &i,int l,int r,int x){
        tr[i=++tt]=tr[la];++tr[i].sz;tr[i].len=r-l+1;
        if (l==r){tr[i].ls=tr[i].rs=0;return;}
        int mid=(l+r)>>1;
        if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x);
        else ins(tr[la].r,tr[i].r,mid+1,r,x);
        if (!tr[i].l){tr[tr[i].l].len=mid-l+1;tr[tr[i].l].ls=tr[tr[i].l].rs=-(mid-l+1);}
        if (!tr[i].r){tr[tr[i].r].len=r-mid;tr[tr[i].r].ls=tr[tr[i].r].rs=-(r-mid);}
        tr[i]=updata(tr[i],tr[tr[i].l],tr[tr[i].r]);}
    use ask(int i,int l,int r,int ll,int rr){
        if (ll<=l&&r<=rr){
            if (!i){tr[i].len=r-l+1;tr[i].ls=tr[i].rs=-(r-l+1);}
            return tr[i];
        }int mid=(l+r)>>1;use x,y;
        bool f1,f2;f1=f2=false;
        if (ll<=mid){x=ask(tr[i].l,l,mid,ll,rr);f1=true;}
        if (rr>mid){y=ask(tr[i].r,mid+1,r,ll,rr);f2=true;}
        if (!f1) return y;
        if (!f2) return x;
        return updata(x,x,y);}
    bool judge(int x){
        int ll=0,rr=0,mm;use ci;
        if (x==1) return true;
        ci=ask(rt[x-1],1,n,zh[1]+1,zh[2]+1);
        mm=ci.sz-(ci.len-ci.sz);
        if (zh[0]<zh[1]){
            ci=ask(rt[x-1],1,n,zh[0]+1,zh[1]);
            ll=ci.rs;
        }if (zh[2]<zh[3]){
            ci=ask(rt[x-1],1,n,zh[2]+2,zh[3]+1);
            rr=ci.ls;
        }return ll+mm+rr<=0;}
    int main(){
        int m,i,j,l,r,mid,x;scanf("%d",&n);
        for (i=1;i<=n;++i){scanf("%d",&ai[i]);bi[i]=ai[i];}
        sort(bi+1,bi+n+1);bz=unique(bi+1,bi+n+1)-bi-1;
        for (i=1;i<=n;++i){
            ai[i]=upper_bound(bi+1,bi+bz+1,ai[i])-bi-1;
            ni[i]=(uu){ai[i],i};
        }sort(ni+1,ni+n+1);
        for (i=1;i<=n;++i){
            if (i&&ni[i].x==ni[i-1].x) ins(rt[ni[i].x],rt[ni[i].x],1,n,ni[i].po);
            else ins(rt[ni[i].x-1],rt[ni[i].x],1,n,ni[i].po);
        }scanf("%d",&m);
        for (x=0LL,i=1;i<=m;++i){
            for (j=0;j<4;++j){
                scanf("%d",&zh[j]);zh[j]=(zh[j]+x)%n;
            }sort(zh+0,zh+4);
            l=1;r=bz;
            while(l<=r){
                mid=(l+r)>>1;
                if (judge(mid)){l=mid+1;x=mid;}
                else r=mid-1;
            }x=bi[x];printf("%d
    ",x);
        }
    }
    View Code
  • 相关阅读:
    【翻译】JavaScript循环和作用域
    【js-xlsx和file-saver插件】前端导出数据到excel
    H5混合开发进阶
    一分钟搞定vue全局注册事件eventBus
    cordova+vue构建app进阶
    LayaBox进阶之UI管理器
    Layabox进阶之资源加载
    H5混合开发进阶之inspect调试
    认识json
    正则学习记录
  • 原文地址:https://www.cnblogs.com/Rivendell/p/4347645.html
Copyright © 2020-2023  润新知