树套树

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

 

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\n",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\n",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\n",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\n",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("\n");
    }
}
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\n",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\n",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\n");continue;}
        j=upper_bound(bi+1,bi+bz+1,cc)-bi-1;
        if (bi[j]!=cc){printf("Yes\n");continue;}
        if (ask(rt[x+k-2],rt[y],1,bz,j)) printf("No\n");
        else printf("Yes\n");
    }
}
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\n");
            else printf("%d\n",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\n");
            else printf("%d\n",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\n",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\n",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\n",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\n",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\n",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\n",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\n",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\n",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!\n");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\n",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\n",qrank(1,1,n,l,r,k-1)+1);
            if (opt==2) printf("%d\n",qkth(l,r,k));
            if (opt==4) printf("%d\n",qpre(1,1,n,l,r,k));
            if (opt==5) printf("%d\n",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\n",x);
    }
}
View Code

 

posted @ 2015-03-18 16:40  Rivendell  阅读(406)  评论(0编辑  收藏  举报