码迷,mamicode.com
首页 > 其他好文 > 详细

[CQOI2011]动态逆序对

时间:2018-01-18 21:14:57      阅读:159      评论:0      收藏:0      [点我收藏+]

标签:路径   pre   sum   putchar   clu   amp   ret   while   std   

原题点这里

我们发现这道题可以用树状数组套权值线段树(主席树的一些优化)

(不会点这里) 我感觉我这样写下去朴素的主席树要不会写了。

我们发现一个点对答案的贡献有两部份,在其之前比其大的,在其之后比他小的。

我们每次删除一个点,把其对答案的贡献减去就好了。

我们考虑一下这样所费的空间,我们知道删除时是按原路径遍历,不会加空间。我们只考虑加点的空间开销。我们每次加一个点,期望我们访问log n个线段树,每个线段树log n个点,那么就是O(nlog n^2)的空间复杂度。5W*16*16=1280W?事实证明是不到的,因为我们不是每次都会开出logN^2个点。反正能开多大就开多大吧。

就酱紫。

#include<bits/stdc++.h>
#define N 8500007 
#define M 100007
#define Mid (l+r>>1)
#define L(x) (x&-x)
#define LL long long
using namespace std;
int siz[N],ls[N],rs[N],si,n,tot,ne[M],m,X,a[M],n2[M],n3[M],to1;
LL ans;
#define sight(c) (‘0‘<=c&&c<=‘9‘)
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar())x=x*10+c-48;
}
void write(LL x){if (x<10) {putchar(0+x); return;} write(x/10); putchar(0+x%10);}
inline void writeln(LL x){ if (x<0) putchar(-),x*=-1; write(x); putchar(\n); }
void ins(int l,int r,int no,int q,int dla){
    siz[no]+=dla; if (l==r) return;
    if (Mid>=q) {if (!ls[no]) ls[no]=++si;ins(l,Mid,ls[no],q,dla);}
    else {if (!rs[no]) rs[no]=++si;ins(Mid+1,r,rs[no],q,dla);}
}
void add(int x,int i,int dla){for (;x<=n;x+=L(x)) ins(1,n,x,i,dla);}
int ask(int l,int r,int q){
    if (l==r) return 0;
    if (Mid<q)  {
        for (int i=1;i<=tot;i++) ne[i]=rs[ne[i]]; return ask(Mid+1,r,q);
    }
    int sum=0;
    for (int i=1;i<=tot;i++) sum+=siz[rs[ne[i]]],ne[i]=ls[ne[i]];
    return sum+ask(l,Mid,q);
}
int Ask(int l,int r,int q){
    if (l==r) return 0;
    if (q<=Mid) {
        for (int i=1;i<=tot;i++) n2[i]=ls[n2[i]];
        for (int i=1;i<=to1;i++) n3[i]=ls[n3[i]]; return Ask(l,Mid,q); 
    }
    LL sum=0;
    for (int i=1;i<=tot;i++) sum-=siz[ls[n2[i]]],n2[i]=rs[n2[i]];
    for (int i=1;i<=to1;i++) sum+=siz[ls[n3[i]]],n3[i]=rs[n3[i]];
    return sum+Ask(Mid+1,r,q);
}
int query(int r,int val){
    for (tot=0;r;r-=L(r)) ne[++tot]=r;
    return ask(1,n,val);
}
int Query(int r,int val){
    int y=n;
    for (tot=0;r;r-=L(r)) n2[++tot]=r;
    for (to1=0;y;y-=L(y)) n3[++to1]=y;
    return Ask(1,n,val);
}
int main () {
    freopen("inverse.in","r",stdin);
    freopen("inverse.out","w",stdout);
    read(n); read(m); si=n;
    for (int i=1;i<=n;i++) 
        read(X),add(i,X,1),a[X]=i;
    for (int i=1;i<=n;i++) 
      ans+=query(a[i],i); 
    while (m--) {
        writeln(ans);read(X);
        ans-=Query(a[X],X)+query(a[X],X);
        add(a[X],X,-1);
    }return 0;
}

 

[CQOI2011]动态逆序对

标签:路径   pre   sum   putchar   clu   amp   ret   while   std   

原文地址:https://www.cnblogs.com/rrsb/p/8312971.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!