您的位置:首页 > 其它

bzoj3295[Cqoi2011]动态逆序对(cdq分治||可持久化线段树)

2016-04-25 22:01 555 查看
题目链接:点这里!!!

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec Memory Limit: 128 MB

Submit: 3043 Solved: 967

[Submit][Status][Discuss]

Description

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

Input

输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下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

做法1:cdq分治
1、首先我们看到求逆序对,很容易想到用树状数组去求逆序对,这里在有修改的情况下,我们一样用树状数组来做,只是再加上cdq分治。

2、首先我们倒过来做,把删除看成添加操作。比如例题:它的添加顺序是(数值):3(没有被删除的数先添加)、2、4、1、5。

3、然后我们来定义一个数组a,三个属性id(添加操作的顺序),val(数值的大小),pv(在序列里的位置)。

4、按照id来排序。 然后我们对val来分治,用pv来更新和求值。

5、注意要更新两次和求值两次,具体看代码,再叠加起来就可。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<bitset>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<cstdlib>
#include<cmath>
#define PI 2*asin(1.0)
#define LL long long
#define pb push_back
#define pa pair<int,int>
#define clr(a,b) memset(a,b,sizeof(a))
#define lson lr<<1,l,mid
#define rson lr<<1|1,mid+1,r
#define bug(x) printf("%d++++++++++++++++++++%d\n",x,x)
#define key_value ch[ch[root][1]][0]C:\Program Files\Git\bin
const int  MOD = 1E9+7;
const LL N = 1E5+15;
const int maxn = 1e4+1000;
const int letter = 130;
const int INF = 1e17;
const double pi=acos(-1.0);
const double eps=1e-8;
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,k,val
,c
,vis
,pp
;
LL ans
;
vector<int>ps;
struct node{
int id,val,pv;
}a
,b
;
bool cmp(node a,node b){
return a.id<b.id;
}
int lowbit(int x){return x&(-x);}
void update(int i,int v){
for(int x=i;x<=n;x+=lowbit(x)) pp[x]+=v;
}
int getsum(int i){
int ans=0;
for(int x=i;x;x-=lowbit(x)) ans+=pp[x];
return ans;
}
void solve(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;/// val
for(int i=l;i<=r;i++){
if(a[i].val<=mid) update(a[i].pv,1);
else ans[a[i].id]+=1ll*(getsum(n)-getsum(a[i].pv));
}
for(int i=l;i<=r;i++) if(a[i].val<=mid) update(a[i].pv,-1);
for(int i=l;i<=r;i++){
if(a[i].val>mid) update(a[i].pv,1);
else ans[a[i].id]+=1ll*getsum(a[i].pv-1);
}
for(int i=l;i<=r;i++) if(a[i].val>mid) update(a[i].pv,-1);
int ll=l;
for(int i=l;i<=r;i++) if(a[i].val<=mid) b[ll++]=a[i];
for(int i=l;i<=r;i++) if(a[i].val>mid) b[ll++]=a[i];
for(int i=l;i<=r;i++) a[i]=b[i];
solve(l,mid);
solve(mid+1,r);
}
int main(){
ps.clear();
int x;
n=read(),k=read();
for(int i=1;i<=n;i++){
a[i].val=read(),a[i].pv=i;
c[a[i].val]=i;
}
for(int i=1;i<=k;i++){
x=read();
vis[c[x]]=1;
ps.pb(c[x]);
}
int cnt=0;
for(int i=1;i<=n;i++){
if(!vis[i]) a[i].id=++cnt;
}
for(int i=ps.size()-1;i>=0;i--) a[ps[i]].id=++cnt;
sort(a+1,a+cnt+1,cmp);
solve(1,n);
for(int i=1;i<=n;i++){
ans[i]+=ans[i-1];
}
for(int i=k;i>=1;i--){
printf("%lld\n",ans[n-k+i]);
}
return 0;
}
/*
5 4
1 5 3 4 2
5 1 4 2
5 5
5 4 3 2 1
5 4 3 2 1
*/


做法2:可持久化线段树

这道题做了一天,超了一天的内存,傻逼了。= =,然后优化一下就过了,一脸蒙逼。

1、注意一下数据范围:n<=100000 m<=50000。一开始做的时候我用的是n个数去维护可持久化线段树,各种超内存。

2、仔细想了一下我们有n-m个数没有变过,所以我们离线处理。其他m个点,插入用可持久化线段树即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<bitset>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<cstdlib>
#include<cmath>
#define PI 2*asin(1.0)
#define LL long long
#define pb push_back
#define pa pair<int,int>
#define clr(a,b) memset(a,b,sizeof(a))
#define lson lr<<1,l,mid
#define rson lr<<1|1,mid+1,r
#define bug(x) printf("%d++++++++++++++++++++%d\n",x,x)
#define key_value ch[ch[root][1]][0]C:\Program Files\Git\bin
const int  MOD = 1E9+7;
const LL N = 1E5+15;
const int maxn = 1e4+1000;
const int letter = 130;
const int INF = 1e17;
const double pi=acos(-1.0);
const double eps=1e-8;
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,k,val
,w
,c
,d
;
int sum[N*100],ls[N*100],rs[N*100],root
,siz,ps
,vis
;
int pl[50],pr[50],aa,bb;
int vl
,vr
;
vector<LL>pp;
int lowbit(int x){
return x&(-x);
}
void update(int i,int val){
for(int x=i;x<=n;x+=lowbit(x)) ps[x]+=val;
}
int getsum(int i){
int ans=0;
for(int x=i;x>0;x-=lowbit(x)) ans+=ps[x];
return ans;
}
void insert(int ll,int rr,int x,int &y,int v,int add){
y=++siz;
sum[y]=sum[x]+add;
ls[y]=ls[x],rs[y]=rs[x];
if(ll==rr) return;
int mid=(ll+rr)>>1;
if(v<=mid) insert(ll,mid,ls[x],ls[y],v,add);
else insert(mid+1,rr,rs[x],rs[y],v,add);
}
int query(int l,int r,int w){ ///1 n w
int suml=0,sumr=0;
if(l==r){
for(int i=0;i<aa;i++) suml+=sum[pl[i]];
for(int i=0;i<bb;i++) sumr+=sum[pr[i]];
return sumr-suml;
}
int mid=(l+r)>>1;
for(int i=0;i<aa;i++) suml+=sum[ls[pl[i]]];
for(int i=0;i<bb;i++) sumr+=sum[ls[pr[i]]];
if(w<=mid){
for(int i=0;i<aa;i++) pl[i]=ls[pl[i]];
for(int i=0;i<bb;i++) pr[i]=ls[pr[i]];
return query(l,mid,w);
}
else {
for(int i=0;i<aa;i++) pl[i]=rs[pl[i]];
for(int i=0;i<bb;i++) pr[i]=rs[pr[i]];
return sumr-suml+query(mid+1,r,w);
}
}
void init(){
pp.clear();
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
val[i]=read();
w[val[i]]=i; ///at
}
for(int i=1;i<=k;i++){
int x;
x=read();
d[i]=x;
c[i]=w[x];
vis[c[i]]=1;
}
LL ans=0;
int cnt=0;
for(int i=1;i<=n;i++){
if(!vis[i]){ ///no
update(val[i],1);
ans+=1ll*(cnt-getsum(val[i]-1));
cnt++;
}
else {  /// shanchu
vl[i]=cnt-getsum(val[i]-1);
}
}
cnt=0;
clr(ps,0);
for(int i=n;i>=1;i--){
if(!vis[i]){
update(val[i],1);
}
else {
vr[i]=getsum(val[i]-1);
}
}
clr(ps,0);
for(int i=k;i>=1;i--){
aa=bb=0;
for(int j=c[i];j<=n;j+=lowbit(j)) insert(1,n,root[j],root[j],d[i],1);
for(int j=c[i]-1;j;j-=lowbit(j)) pr[bb++]=root[j];
update(c[i],1);
ans+=1ll*((LL)getsum(c[i]-1)-(LL)query(1,n,d[i]-1)+vl[c[i]]);
aa=bb=0;
for(int j=c[i];j;j-=lowbit(j)) pl[aa++]=root[j];
for(int j=n;j;j-=lowbit(j)) pr[bb++]=root[j];
ans+=1ll*(vr[c[i]]+query(1,n,d[i]-1));
pp.pb(ans);
}
for(int i=pp.size()-1;i>=0;i--) printf("%lld\n",pp[i]);
}
int main(){
// freopen("input.txt","r",stdin);
// freopen("out.txt","w",stdout);
init();
return 0;
}
/*
5 4
1 5 3 4 2
5 1 4 2
10 5
10 9 8 7 6 5 4 3 2 1
5 4 3 2 1
5 3
5 4 3 2 1
3 2 1
*/

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: