Hdu5737-Differencia(有序表线段树)

题意很直观,我就不说了。

解析:这是我以前没有接触过的线段树类型,有序表线段树,每个节点申请了两段空间,主要是为了保存左边儿子会有多少比v小的,右边儿子会有多少比v小

的,所以在建树过程中要归并排序。可能我讲起来比较难懂,详见代码,我给了注释。

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef __int64 LL;
#define e tree[id]
#define lson tree[id*2]
#define rson tree[id*2+1]
const int mod=1e9+7;
const int maxn=100005;
const int C=~(1<<31),M=(1<<16)-1;
int rnd(int last,int &a,int &b)
{
    a=(36969+(last>>3))*(a&M)+(a>>16);
    b=(18000+(last>>3))*(b&M)+(b>>16);
    return (C&((a<<16)+b))%1000000000;
}
int n,m,A,B,a[maxn],b[maxn],c[maxn],xs[maxn];
bool cmp(const int& x,const int& y){ return b[x]<b[y]; }
int data[maxn<<6],*p;
int* New(int len){ p+=len; return p-len; } //静态的申请空间
void init() //离散化
{
    for(int i=0;i<n;i++) xs[i]=b[i];
    sort(xs,xs+n);
    for(int i=0;i<n;i++) b[i]=upper_bound(xs,xs+n,b[i])-xs-1;
    for(int i=0;i<n;i++) a[i]=upper_bound(xs,xs+n,a[i])-xs-1;
    p=data; //在最开始的位置
}
struct Tree
{
    int le,ri,d,sum;
    int *lid,*rid;
    void Set(int v){ d=v; sum=v+1; }
    int GoLe(int v){ return v==-1?v:lid[v]; }  //左边有多少<=v的
    int GoRi(int v){ return v==-1?v:rid[v]; }  //右边有多少<=v的
}tree[4*maxn];
void pushup(int id){ e.sum=lson.sum+rson.sum; } //取和
void pushdown(int id)
{
    if(e.d!=-2)  //延迟更新
    {
        lson.Set(e.GoLe(e.d));
        rson.Set(e.GoRi(e.d));
        e.d=-2;
    }
}
void Build_tree(int id,int le,int ri)
{
    e.le=le,e.ri=ri,e.d=-2;
    e.lid=New(ri-le+1);  //左右都要申请空间
    e.rid=New(ri-le+1);
    if(le==ri){ e.sum=(a[le]>=b[le]); return; }
    int mid=(le+ri)/2;
    Build_tree(id*2,le,mid);
    Build_tree(id*2+1,mid+1,ri);
    pushup(id);
    int ll=mid-le+1,rl=ri-mid;
    int *vl=b+le,*vr=b+mid+1;
    int i=0,j=0,cnt=0;
    while(i<ll&&j<rl)  //归并排序
    {
        if(vl[i]<vr[j])  //左边小于右边
        {
            e.lid[cnt]=i;
            e.rid[cnt]=j-1;
            c[cnt++]=vl[i++];
        }
        else
        {
            e.lid[cnt]=i-1;
            e.rid[cnt]=j;
            c[cnt++]=vr[j++];
        }
    }
    while(i<ll)  //左边没完的
    {
        e.lid[cnt]=i;
        e.rid[cnt]=j-1;
        c[cnt++]=vl[i++];
    }
    while(j<rl)  //右边没完的
    {
        e.lid[cnt]=i-1;
        e.rid[cnt]=j;
        c[cnt++]=vr[j++];
    }
    int k=0;
    for(int i=le;i<=ri;i++) b[i]=c[k++];
}
void Update(int id,int x,int y,int v) //更新
{
    int le=e.le,ri=e.ri;
    if(x<=le&&ri<=y){ e.Set(v); return; } //在区间内
    pushdown(id);
    int mid=(le+ri)/2;
    if(x<=mid) Update(id*2,x,y,e.GoLe(v)); //左边GoLe代表左边有多少<=v的
    if(y>mid) Update(id*2+1,x,y,e.GoRi(v));//右边同理
    pushup(id);
}
int Query(int id,int x,int y)  //查询
{
    int le=e.le,ri=e.ri;
    if(x<=le&&ri<=y) return e.sum;  //在区间内直接返回值
    pushdown(id);  //延迟更新
    int mid=(le+ri)/2;
    int ret=0;
    if(x<=mid) ret+=Query(id*2,x,y);   //加上左边
    if(y>mid)  ret+=Query(id*2+1,x,y); //加上右边
    return ret;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d",&n,&m,&A,&B);
        for(int i=0;i<n;i++) scanf("%d",&a[i]); //输入
        for(int i=0;i<n;i++) scanf("%d",&b[i]);
        init();
        Build_tree(1,0,n-1); //建树
        int last=0,ret=0;
        for(int i=1;i<=m;i++)
        {
            int l=rnd(last,A,B)%n;
            int r=rnd(last,A,B)%n;
            int x=rnd(last,A,B)+1;
            if(l>r) swap(l,r);
            if((l+r+x)%2)  //为奇数是插入
            {
                x=upper_bound(xs,xs+n,x)-xs-1;
                Update(1,l,r,x);
            }
            else  //否则查询
            {
                last=Query(1,l,r);
                ret=(ret+(LL)i*last%mod)%mod;
            }
        }
        printf("%d
",ret);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/wust-ouyangli/p/5706298.html