3295: [Cqoi2011]动态逆序对 CDQ分治
2016-03-01 11:21
423 查看
依稀记得400题的时候就是做的这道题,写的树套树结果电脑死机了就没重新写。
由于此题可以离线,把删除操作改为逆序插入操作。
我们看一下样例,可以得到这样三个序列。
n:1 2 3 4 5
x:3 5 4 1 2
y:3 2 4 1 5
其中n为插入的顺序(就是删除的逆序),x为这个值在原序列中的位置,y为按顺序添加的数(不足补齐)。
然后考虑产生逆序对的情况:如果当前加入的数,之前已经加入一个比它小的数,且这个数的位置在它的后面,形式的说就是同时满足:ni<nj,xi>xj,yi<yjn_ix_j,y_i。而如果当前加入的数,之前已经加入一个比它大的数,且这个数的位置在它的前面,形式的说就是同时满足ni<nj,xi<xj,yi>yjn_iy_j,可以发现这两个问题是类似的,我们可以通过令xi=n−xi+1x_i=n-x_i+1或令yi=n−yi+1y_i=n-y_i+1转化为三维偏序的问题,然后就用CDQ分治解决就好了。
统计答案维护答案的前缀和。
由于此题可以离线,把删除操作改为逆序插入操作。
我们看一下样例,可以得到这样三个序列。
n:1 2 3 4 5
x:3 5 4 1 2
y:3 2 4 1 5
其中n为插入的顺序(就是删除的逆序),x为这个值在原序列中的位置,y为按顺序添加的数(不足补齐)。
然后考虑产生逆序对的情况:如果当前加入的数,之前已经加入一个比它小的数,且这个数的位置在它的后面,形式的说就是同时满足:ni<nj,xi>xj,yi<yjn_ix_j,y_i。而如果当前加入的数,之前已经加入一个比它大的数,且这个数的位置在它的前面,形式的说就是同时满足ni<nj,xi<xj,yi>yjn_iy_j,可以发现这两个问题是类似的,我们可以通过令xi=n−xi+1x_i=n-x_i+1或令yi=n−yi+1y_i=n-y_i+1转化为三维偏序的问题,然后就用CDQ分治解决就好了。
统计答案维护答案的前缀和。
[code]#include<iostream> #include<cstdio> #include<algorithm> #define lowbit(i) (i&(-i)) #define ll long long #define N 100005 using namespace std; int n,m; int x ,u ,w ,pos ,tree ; ll ans ; struct node {int x,y,z; ll ans;} a ,b ; inline int read() { int a=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } inline void add(int x,int val) { for (int i=x;i<=n;i+=lowbit(i)) tree[i]+=val; } inline int query(int x) { int tmp=0; for (int i=x;i;i-=lowbit(i)) tmp+=tree[i]; return tmp; } void solve(int l,int r) { if (l==r) return; int mid=l+r>>1,p1=l,p2=mid+1; solve(l,mid); solve(mid+1,r); while (p2<=r) { while (p1<=mid&&a[p1].y<a[p2].y) { add(a[p1].z,1); p1++; } a[p2].ans+=query(a[p2].z); p2++; } for (int i=l;i<p1;i++) add(a[i].z,-1); p1=l; p2=mid+1; for (int i=l;i<=r;i++) if (p1<=mid&&(p2>r||a[p1].y<a[p2].y)) b[i]=a[p1++]; else b[i]=a[p2++]; for (int i=l;i<=r;i++) a[i]=b[i]; } int main() { n=read(); m=read(); for (int i=1;i<=n;i++) w[read()]=i; for (int i=1;i<=m;i++) x[i]=read(),u[x[i]]=1; for (int i=1,k=m;i<=n;i++) if(!u[i]) x[++k]=i; for (int i=n;i;i--) a[n-i+1]=(node){n-i+1,n-w[x[i]]+1,x[i]}; solve(1,n); for (int i=1;i<=n;i++) ans[a[i].x]+=a[i].ans; for (int i=n;i;i--) a[n-i+1]=(node){n-i+1,w[x[i]],n-x[i]+1}; solve(1,n); for (int i=1;i<=n;i++) ans[a[i].x]+=a[i].ans; for (int i=1;i<=n;i++) ans[i]+=ans[i-1]; for (int i=n;i>n-m;i--) printf("%lld\n",ans[i]); return 0; }
相关文章推荐
- Spring的任务调度
- Build.VERSION.SDK_INT
- JS的内建函数reduce
- ;function($,undefined) 前面的分号是什么用处
- Linux find 用法示例
- js 回调函数
- 解析HTML5中的新功能本地存储localStorage
- PackageManager使用
- PL/SQL之触发器谓词
- android电话自动接听/挂断
- POI 操作office2007
- hadoop伪分布式ha框架搭建
- android 电容屏(一):电容屏基本原理篇
- 我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
- Linux下TFTP服务的安装、配置和操作
- 2016年Android市场的8个大胆预测
- Java中重载与重写的区别
- 31.绿豆蛙的归宿(拓扑排序)
- 31.绿豆蛙的归宿(拓扑排序)
- 31.绿豆蛙的归宿(拓扑排序)