zoukankan      html  css  js  c++  java
  • [BZOJ3295][Cqoi2011]动态逆序对 CDQ分治&树套树

    3295: [Cqoi2011]动态逆序对

    Time Limit: 10 Sec  Memory Limit: 128 MB

    Description

    对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

    Input

    输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
     

    Output

     
    输出包含m行,依次为删除每个元素之前,逆序对的个数。

    Sample Input

    5 4
    1
    5
    3
    4
    2
    5
    1
    4
    2

    Sample Output

    5
    2
    2
    1

    样例解释
    (1,5,3,4,2) (1,3,4,2) (3,4,2) (3,2) (3)。

    HINT

    N<=100000 M<=50000

    题解:

    啊啊啊这道题真的是差点把自己打死

    跟zyfdalao分享了一下思路然后他就按我的思路A了可是我自己却不会了

    如果你是树套树玩家,请移步:http://www.cnblogs.com/TSHugh/p/7001884.html

    在具体写题解之前,我要分享一个面对瓶颈很好的做法(我这样反复理了三遍,思路一遍比一遍清晰...遇到瓶颈的时候真的很推荐这样做):

    思路不通了把所有已知条件再列出来一边,写清变量之间的关系和限制,并且针对每一个条件写出解决方案和打法

    感觉凌乱的代码段就删掉重打一遍,用的总时间一定会比死调下去少

    首先一个小优化:把val变成n-val+1,这样原来的逆序对就变成了现在的顺序对,个人感觉更加好操作.

    我们考虑,我们可以利用树状数组求出一开始总的逆序对数,并且同时利用树状数组求出以每个位置为开头和结尾的逆序对个数,

    我们设num数组表示这个数目,num[0][i]表示以i位置结尾的逆序对个数,num[1][i]表示以i位置开头的逆序对个数.

    这样的话,每次我们先输出当前的总数,再减去num[0][i]+num[1][i],然后...

    我们发现有一些不对,如果原来形成的逆序对的另外那个数已经被删除了,那么这一对就不应该被减去

    所以我们考虑再计算一个delta表示这个增量:

    其中delta[0][i]表示以i位置结尾,但是另外那个数比i删除的早的逆序对个数,

    delta[1][i]表示以i位置开头,但是另外那个数比i删除的早的逆序对个数.

    接下来我们考虑什么样的数对(i,j)会被记入delta

    如果设i位置的数的删除时间是tim[i](没有被删除的数的删除时间顺次设成m+1~n即可,他们的delta不会被记入答案)

    设i位置的数值是val[i](这里的val已经进行了n-val+1取反操作)

    那么delta[0][i]里的数应该满足:

    tim[i]>tim[j]

    val[i]>val[j]

    i>j

    delta[1][i]里的数应该满足:

    tim[i]>tim[j]

    val[i]<val[j]

    i<j

    那么上面这两组约束条件(尤其是第一组)很像一个三维偏序的统计问题.

    的确如此,我们只需要用CDQ分治统计一下符合条件的数对数即可.

    拿我的思路来说,我们可以让时间有序,给下标排序,用树状数组维护权值val,

    这样跑两遍CDQ统计符合条件的偏序对数,就能分别计算delta[0][i]和delta[1][i]

    最后输出答案时,先输出答案ans,然后给ans减去num[0][i]+num[1][i],再加上delta[0][i]+delta[1][i],这样就能正确的维护逆序对数了,

    代码实现:

     1 #include <cstring>
     2 #include <cstdio>
     3 #include <algorithm>
     4 using namespace std;
     5 typedef long long LL;
     6 const int N=100010,M=50010;
     7 struct node
     8 {
     9     int tim,val,pos;
    10     node (int a=0,int b=0,int c=0){tim=a,val=b,pos=c;}
    11 }q[N];
    12 int cnt,n,m,a[N],match[N],step[M];
    13 bool vis[N];
    14 LL delta[2][N],num[2][N],bit[N];
    15 inline int lowbit(int a){return a&-a;}
    16 inline void add(int a,LL b)
    17     {while(a<=n)bit[a]+=b,a+=lowbit(a);}
    18 inline LL sum(int a)
    19     {LL ret=0;while(a)ret+=bit[a],a-=lowbit(a);return ret;}
    20 inline void gsum0()
    21 {
    22     for(register int i=1;i<=n;++i)
    23         num[0][i]=sum(a[i]),add(a[i],1);
    24     memset(bit,0,sizeof(bit));
    25 }
    26 inline void gsum1()
    27 {
    28     for(register int i=n;i;--i)
    29         num[1][i]=sum(a[i]),add(1,1),add(a[i],-1);
    30     memset(bit,0,sizeof(bit));
    31 }
    32 inline void readin()
    33 {
    34     register int i;scanf("%d%d",&n,&m);
    35     for(i=1;i<=n;++i)
    36         scanf("%d",&a[i]),a[i]=n-a[i]+1,match[a[i]]=i;
    37     gsum0();gsum1();
    38     for(i=1;i<=m;++i)
    39         scanf("%d",&step[i]),step[i]=n-step[i]+1,vis[step[i]]=1;
    40 }
    41 inline bool mt1(const node &a,const node &b){return a.pos<b.pos;}
    42 inline bool mt2(const node &a,const node &b){return a.tim<b.tim;}
    43 inline void CDQ0(int l,int r)
    44 {
    45     if(l==r)return;
    46     register int mi=l+r>>1,i;
    47     CDQ0(l,mi);CDQ0(mi+1,r);
    48     sort(q+l,q+r+1,mt1);
    49     for(i=l;i<=r;++i)
    50         if(q[i].tim<=mi)add(q[i].val,1);
    51         else delta[0][q[i].tim]+=sum(q[i].val);
    52     for(i=l;i<=r;++i)
    53         if(q[i].tim<=mi)add(q[i].val,-1);
    54 }
    55 inline void CDQ1(int l,int r)
    56 {
    57     if(l==r)return;
    58     register int mi=l+r>>1,i;
    59     CDQ1(l,mi);CDQ1(mi+1,r);
    60     sort(q+l,q+r+1,mt1);
    61     for(i=r;i>=l;--i)
    62         if(q[i].tim<=mi)add(1,1),add(q[i].val,-1);
    63         else delta[1][q[i].tim]+=sum(q[i].val);
    64     for(i=r;i>=l;--i)
    65         if(q[i].tim<=mi)add(1,-1),add(q[i].val,1);
    66 }
    67 int main()
    68 {
    69     register int i;readin();
    70     for(i=1;i<=m;++i)
    71         q[++cnt]=node(cnt,step[i],match[step[i]]);
    72     for(i=1;i<=n;++i)
    73         if(!vis[a[i]])q[++cnt]=node(cnt,a[i],i);
    74     CDQ0(1,n),sort(q+1,q+n+1,mt2),CDQ1(1,n);
    75     LL ans=0;int pos;
    76     for(i=1;i<=n;++i)ans+=num[0][i];
    77     for(i=1;i<=m;++i)
    78     {
    79         printf("%lld
    ",ans),pos=match[step[i]],
    80         ans-=(num[0][pos]+num[1][pos]),
    81         ans+=(delta[0][i]+delta[1][i]);
    82     }
    83 }
  • 相关阅读:
    jquery动画效果---animate()--滚屏
    一个前端的自我修养
    开发和测试
    jquery.find()
    c99和c++11的差异之一
    容器经典图
    C/C++中的##用法
    【心学.悟道】千圣皆过影,良知乃吾师
    memcpy, memset代码改写的方式
    三大软件原则
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7617464.html
Copyright © 2011-2022 走看看