统计逆序对--函数式线段树

[题目描述]

众所周知,lyp喜欢以用各种方式折磨别人为乐,这次,他趁 wars不在时在他的电脑上
挂了一把神奇的锁,这把锁需要一串巨长无比的数字密码才可以解开,这 个密码由 lyp 自
己保管,这样 wars 就没法 Kingdom  Rush 了。但 wars 设法从 lyp 的脑袋中挖出了有关密码
的信息,这些信息是一列非负整数{An}。而解开密 码锁的方式是首先输入这列这数的逆序
对数,而后依次会在 wars的电脑屏幕上显示两个数字 p,q,你则需要输入将整数列中第 p个
整数替换成q后整个数列的逆序 对数,这样的询问会有 m个。当然,这样的替换只对这一
次的询问有效。现在 wars急着去打Kingdom Rush,因此将打开电脑的任务交给了你。 

[数据范围]

 n<=50000,m<=50000 
0<=序列的每一元素(包括询问中的)<=100000 
保证询问合法。 

[题解]

      这道题目可以用离线算法做.将询问排序后就可以用一个树状数组搞出来了.

      由于解决这道问题要牵扯到查询区间内某一范围内的数的个数,所以也可以用函数式线段树(不过常数被树状数组完爆).总之,这还是一道不错的函数式线段树基础题的.

      讲一下函数式的思想:由于线段树每次修改只要改log(n)个节点,所以每次新建log(n)个节点即可.因为每次root都会被更新,所以直接开一个数组记住每次的root,查询时直接自顶向下的查询即可.

      图就不画了(太丑了),不过这东西实现起来还是比较容易的,代码也不长.唯一的缺点是需要的空间太多了.

Code:

program lyp;
type int=longint;
const null=-1;
var
        i,j,k,m,ans,n,tot,x,y:int;
        s,ls,rs,ll,rr:array[0..2000000]of int;
        root,a:array[0..50000]of int;

function build(l,r:int):int;var mid,now:int;
begin
        if l=r-1 then begin
                inc(tot);ls[tot]:=null;rs[tot]:=null;ll[tot]:=l;rr[tot]:=r;
                exit(tot);
        end;
        mid:=(l+r)>>1;
        inc(tot);now:=tot;
        ll[tot]:=l;rr[tot]:=r;
        ls[now]:=build(l,mid);
        rs[now]:=build(mid,r);
        exit(now);
end;

function ins(x,y:int):int;var mid:int;
begin
        if ll[x]=rr[x]-1 then begin
                inc(tot);s[tot]:=s[x]+1;
                ls[tot]:=null;rs[tot]:=null;
                ll[tot]:=ll[x];rr[tot]:=rr[x];
                exit(tot);
        end;
        if ll[x]>=rr[x] then exit;
        mid:=(ll[x]+rr[x])>>1;
        inc(tot);ins:=tot;
        ls[tot]:=ls[x];rs[tot]:=rs[x];
        ll[tot]:=ll[x];rr[tot]:=rr[x];
        if y>mid then rs[ins]:=ins(rs[x],y)
                else ls[ins]:=ins(ls[x],y);
        s[ins]:=s[ls[ins]]+s[rs[ins]];
end;

function ask(r1,r2,l,r:int):int;var mid:int;
begin
        if(r1=r2)then exit(0);
        if(l<=ll[r1])and(rr[r1]<=r)then exit(s[r2]-s[r1]);
        ask:=0;mid:=(ll[r1]+rr[r1])>>1;
        if(r>mid)then ask:=ask+ask(rs[r1],rs[r2],l,r);
        if(l<mid)then ask:=ask+ask(ls[r1],ls[r2],l,r);
end;

begin
        assign(input,'lyp.in');reset(input);
        assign(output,'lyp.out');rewrite(output);
        root[0]:=build(0,100000);
        read(n,m);
        for i:=1 to n do begin
                read(a[i]);
                inc(a[i]);
                root[i]:=ins(root[i-1],a[i]);
                ans:=ans+ask(root[0],root[i],a[i],100000);
        end;
        writeln(ans);
        for i:=1 to m do begin
                read(x,y);inc(y);
                if y>a[x]then k:=-ask(root[0],root[x-1],a[x],y)+ask(root[x],root[n],a[x]-1,y-1)
                        else k:=ask(root[0],root[x-1],y,a[x])-ask(root[x],root[n],y-1,a[x]-1);
                writeln(ans+k);
        end;
        close(input);close(output);
end.



BY QW

转载请注明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值