Luogu P3157 [CQOI2011]动态逆序对

妖精的绣舞 提交于 2020-02-01 14:49:46

CDQ分治

首先可以将答案分为之前有的逆序对数量+此操作产生的逆序对数量

那么最后前缀和即可

由于题目直接给出的是删除操作,并且加上初始序列的插入操作

有两种不同会对答案造成影响的操作,不容易处理

那么考虑从后往前考虑,那么删除操作就变成插入操作

先将所有要删的数删完剩下的数,把它们作为初始序列

然后进行CQD分治

产生逆序对要求是

$time_{i}<time_{j},val_{i}<val_{j},pos_{i}>pos_{j}$

或$time_{i}<time_{j},val_{i}>val_{j},pos_{i}<pos_{j}$

三维偏序问题

在CDQ分治过程中,正的扫一遍,倒着扫一遍即可

  1 #include <bits/stdc++.h>
  2 #define int long long
  3 using namespace std;
  4 const int N=1e5+100;
  5 int n,m,a[N],b[N],ans[N];
  6 int p[N],w,dfn,tree[N],ti[N];
  7 struct node
  8 {
  9     int ti,pos,val,id;
 10 }sh[N];
 11 node t[N];
 12 int lowbit(int x)
 13 {
 14     return x&(-x);
 15 }
 16 void change(int x,int v)
 17 {
 18     while (x<=n)
 19     {
 20         if (ti[x]!=dfn)
 21         {
 22             ti[x]=dfn;
 23             tree[x]=0;
 24         }
 25         tree[x]+=v;
 26         x+=lowbit(x);
 27     }
 28 }
 29 int query(int x)
 30 {
 31     int ans=0;
 32     while (x>0)
 33     {
 34         if (ti[x]==dfn) ans+=tree[x];
 35         x-=lowbit(x);
 36     }
 37     return ans;
 38 }
 39 int ask(int l,int r)
 40 {
 41     return query(r)-query(l-1);
 42 }
 43 void cdq(int l,int r)
 44 {
 45     if (l==r) return;
 46     int mid=(l+r)>>1;
 47     cdq(l,mid);
 48     cdq(mid+1,r);
 49     dfn++;
 50     int tl=l,tr=mid+1;
 51     for (int i=l;i<=r;i++)
 52     {
 53         if ((tl<=mid && sh[tl].pos<sh[tr].pos) || tr>r)
 54         {
 55             change(sh[tl].val,1);
 56             tl++;
 57         }
 58         else
 59         {
 60             ans[sh[tr].id]+=ask(sh[tr].val+1,n);
 61             tr++;
 62         }
 63     }
 64     dfn++;//注意要将树状数组清零
 65     tl=mid;tr=r;//倒着统计
 66     for (int i=l;i<=r;i++)
 67     {
 68         if ((tl>=l && sh[tl].pos>sh[tr].pos) || tr<=mid)
 69         {
 70             change(sh[tl].val,1);
 71             tl--;
 72         }
 73         else
 74         {
 75             ans[sh[tr].id]+=ask(1,sh[tr].val-1);
 76             tr--;
 77         }
 78     }
 79     tl=l;tr=mid+1;
 80     for (int i=l;i<=r;i++)
 81     {
 82         if ((tl<=mid && sh[tl].pos<sh[tr].pos) || tr>r) t[i]=sh[tl++];
 83         else t[i]=sh[tr++];
 84     }//以pos为第二维从小到大归并排序
 85     for (int i=l;i<=r;i++) sh[i]=t[i];
 86 }
 87 signed main()
 88 {
 89     scanf("%lld%lld",&n,&m);
 90     for (int i=1;i<=n;i++)
 91       scanf("%lld",&a[i]);
 92     for (int i=1;i<=m;i++)
 93       scanf("%lld",&b[i]);
 94     for (int i=1;i<=m;i++) p[b[i]]=1;
 95     for (int i=1;i<=n;i++)
 96     {
 97         if (p[a[i]]==0)
 98         {
 99             w++;
100             sh[w].ti=w;
101             sh[w].val=a[i];
102             sh[w].pos=i;
103             sh[w].id=0;//原序列统计在一起,作为初始值
104         }
105         else p[a[i]]=i;
106     }
107     for (int i=m;i>=1;i--)//进行操作
108     {
109         w++;
110         sh[w].ti=w;
111         sh[w].val=b[i];
112         sh[w].pos=p[b[i]];
113         sh[w].id=m-i+1;
114     }
115     cdq(1,w);
116     for (int i=1;i<=m;i++) ans[i]+=ans[i-1];
117     for (int i=m;i>=1;i--)
118       printf("%lld\n",ans[i]);
119 }
View Code

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!