BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接

方法:树状数组套线段树

解析:

这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队。这道题比那道更要简单。

然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy。引用理论部分。

删除某个数,只要统计它之前还存在的比它大的数的个数,和之后还存在的比它小的数的个数

然后差不多就按照这个意思写。

由于线段树有一种神奇的性质,也就是它的每一层不会有交叉,如果它是平衡的,那么它的每一层都是整个1~n区间。

所以我们可以开个数组sor[log2(100000)][100000]记录每层的每个小区间的排序?我知道这里我说不太明白了,大家可以yy一下,画画图,开这个东西的目的是什么呢?就是为了在某段区间内寻找某个数排第几,所以我们要把每个线段树的点代表的小区间暴力排序,以便2分找。

其次呢我们还需要一个类似sor这个数组的数组,是记录什么的呢?是记录删除的点的!每次删除一个点,要把这个删除的点加到一个树状数组里统计!(我说不懂了!日!我现在的内心是崩溃的!我该怎么说好!看代码吧!!)

好,假设我们统计好了,然后对于每一次这个点,我们需要找他对应的位置,然后将1~k-1进行一次询问!也就是询问排在他前面比他大并且没有被删除的点的个数!这真是中心思想!剩下的自己YY!

再对k+1~n进行一次询问,询问比他小的并且没有被删除的点的个数!

最后去掉这些就好了!

反正我的代码是4s内的!

我不会yy出更好的方法了!

如果能有更好的方法请联系我!

另求hzwer这道题的解法是什么鬼!

多yy!

代码:(附暴力以及数据生成器)

树状数组套线段树AC

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
typedef long long ll;
using namespace std;
int sor[35][N];
int del[35][N];
int a[N];
int s[N];
int n,m;
ll tot;
int lowbit(int x)
{
    return x&(-x);
}
void update(int a[],int x,int v,int U)
{
    while(x<=U)
    {
        a[x]+=v;
        x+=lowbit(x);
    }
}
int get_sum(int a[],int x,int D)
{
    int ret=0;
    while(x>D)
    {
        ret+=a[x];
        x-=lowbit(x);
    }
    return ret;
}
int ask_rnk(int l,int r,int rt,int v)
{
    int ret;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(sor[rt][mid]>=v)r=mid;
        else l=mid+1;
    }
    if(sor[rt][l]>v)l--;
    return l;
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    for(int i=l;i<=r;i++)sor[rt][i]=sor[rt-1][i];
    if(l==r)return;
    build(l,mid,rt+1);
    build(mid+1,r,rt+1);
    sort(sor[rt]+l,sor[rt]+r+1);
}
void query(int l,int r,int L,int R,int v,int rt,int jd)
{
    if(L<=l&&r<=R)
    {
        int pos=ask_rnk(l,r,rt,v);
        int tmp=get_sum(del[rt],pos,l-1);
        if(!jd)
        {
            pos=r-pos,tmp=get_sum(del[rt],r,l-1)-tmp;
        }else pos-=l-1;
        tot-=pos-tmp;
        return;
    }
    if(l>=r)return;
    int mid=(l+r)>>1;
    if(L<=mid)query(l,mid,L,R,v,rt+1,jd);
    if(R>mid)query(mid+1,r,L,R,v,rt+1,jd);
}
void seg_update(int l,int r,int k,int rt,int v)
{
    if(l==r)
    {
        update(del[rt],l,1,r);
        return;
    }
    if(l>r)return;
    int mid=(l+r)>>1;
    if(k<=mid)seg_update(l,mid,k,rt+1,v);
    else seg_update(mid+1,r,k,rt+1,v);
    int pos=ask_rnk(l,r,rt,v);
    update(del[rt],pos,1,r); 
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&sor[0][i]);
        a[sor[0][i]]=i;
        tot+=i-1-get_sum(s,sor[0][i],0);
        update(s,sor[0][i],1,n);
    }
    build(1,n,1);
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        printf("%lld\n",tot);
        query(1,n,1,a[x]-1,x,1,0);
        query(1,n,a[x]+1,n,x,1,1);
        seg_update(1,n,a[x],1,x); 
    } 
}

平衡树套线段树完美TLE(可拿去当暴力对拍)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100100
#define M 50100
#define K 400100
#define S 3000100
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
int n,m,size,ans;
ll tot;
int a[N];
int root[K];
int hash[N];
struct node
{
    int l,r,w,v,size,rnd;
}tr[S];
void pushup(int rt)
{
    tr[rt].size=tr[tr[rt].l].size+tr[tr[rt].r].size+tr[rt].w;
}
void lturn(int &rt)
{
    int t=tr[rt].r;
    tr[rt].r=tr[t].l;
    tr[t].l=rt;
    tr[t].size=tr[rt].size;
    pushup(rt);
    rt=t;
}
void rturn(int &rt)
{
    int t=tr[rt].l;
    tr[rt].l=tr[t].r;
    tr[t].r=rt;
    tr[t].size=tr[rt].size;
    pushup(rt);
    rt=t;
}
void insert(int &rt,int num)
{
    if(!rt)
    {
        rt=++size;
        tr[rt].w=tr[rt].size=1,tr[rt].v=num,tr[rt].rnd=rand();
        return;
    }
    tr[rt].size++;
    if(num==tr[rt].v){tr[rt].w++;return;}
    if(num<tr[rt].v){insert(tr[rt].l,num);if(tr[tr[rt].l].rnd<tr[rt].rnd)rturn(rt);}
    else {insert(tr[rt].r,num);if(tr[tr[rt].r].rnd<tr[rt].rnd)lturn(rt);}
}
void del(int &rt,int num)
{
    if(tr[rt].v==num)
    {
        if(tr[rt].w>1){tr[rt].w--,tr[rt].size--;return;}
        if(tr[rt].l*tr[rt].r==0)rt=tr[rt].l+tr[rt].r;
        else if(tr[tr[rt].l].rnd<tr[tr[rt].r].rnd){rturn(rt),del(rt,num);}
        else {lturn(rt),del(rt,num);}
    }
    else if(num<tr[rt].v){tr[rt].size--;del(tr[rt].l,num);}
    else tr[rt].size--,del(tr[rt].r,num);
}
void q_del(int k,int l,int r,int rt,int num)
{
    del(root[rt],num);
    if(l==r)return;
    int mid=(l+r)>>1;
    if(k<=mid)q_del(k,lson,num);
    else q_del(k,rson,num);
}
void build(int l,int r,int rt,int x,int num)
{
    insert(root[rt],num);
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)build(lson,x,num);
    else build(rson,x,num);
}
void tr_rnk_le(int rt,int x)
{
    if(!rt)return;
    if(tr[rt].v==x){ans+=tr[tr[rt].l].size;return;}
    else if(x<tr[rt].v){tr_rnk_le(tr[rt].l,x);}
    else {ans+=tr[tr[rt].l].size+tr[rt].w;tr_rnk_le(tr[rt].r,x);}
}
void q_rnk_le(int L,int R,int l,int r,int rt,int x)
{
    if(L==l&&r==R){tr_rnk_le(root[rt],x);return;}
    int mid=(l+r)>>1;
    if(R<=mid){q_rnk_le(L,R,lson,x);}
    else if(L>mid){q_rnk_le(L,R,rson,x);}
    else
    {
        q_rnk_le(L,mid,lson,x);
        q_rnk_le(mid+1,R,rson,x);
    }
}
void tr_rnk_bi(int rt,int x)
{
    if(!rt)return;
    if(tr[rt].v==x){ans+=tr[tr[rt].r].size;return;}
    else if(x>tr[rt].v){tr_rnk_bi(tr[rt].r,x);}
    else {ans+=tr[tr[rt].r].size+tr[rt].w;tr_rnk_bi(tr[rt].l,x);}
}
void q_rnk_bi(int L,int R,int l,int r,int rt,int x)
{
    if(L==l&&r==R){tr_rnk_bi(root[rt],x);return;}
    int mid=(l+r)>>1;
    if(R<=mid){q_rnk_bi(L,R,lson,x);}
    else if(L>mid){q_rnk_bi(L,R,rson,x);}
    else
    {
        q_rnk_bi(L,mid,lson,x);
        q_rnk_bi(mid+1,R,rson,x);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),hash[a[i]]=i;
    for(int i=1;i<=n;i++)build(1,n,1,i,a[i]);
    for(int i=1;i<n;i++)ans=0,q_rnk_le(i+1,n,1,n,1,a[i]),tot+=ans;
    for(int i=1;i<=m;i++)
    {
        printf("%lld\n",tot);
        int x;
        scanf("%d",&x);
        int y=hash[x];
        q_del(y,1,n,1,x);
        if(y!=1)ans=0,q_rnk_bi(1,y-1,1,n,1,x),tot-=ans;
        if(y!=n)ans=0,q_rnk_le(y+1,n,1,n,1,x),tot-=ans;
    }
}

数据生成器

#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;
int f[100005],a[100005];
int main()
{
  srand((int)time(0));
  int n=100000,m=50000;
  printf("%d %d\n",n,m);
  for (int i=1;i<=n;i++) a[i]=i;
  int t;
  for (int i=1;i<=1000000;i++) 
  {
    int P=rand()%n+1,Q=rand()%n+1;
    t=a[P];a[P]=a[Q];a[Q]=t;
  }
  for (int i=1;i<=n;i++) printf("%d\n",a[i]);
  f[0]=1;
  for (int i=1;i<=m;i++)
  {
    for (t=0;f[t];t=rand()%n+1);
    f[t]=1;
    printf("%d\n",t);
  } 
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值