对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
题目链接:点这里!!!
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[N],c[N],vis[N],pp[N]; LL ans[N]; vector<int>ps; struct node{ int id,val,pv; }a[N],b[N]; 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 */
这道题做了一天,超了一天的内存,傻逼了。= =,然后优化一下就过了,一脸蒙逼。
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[N],w[N],c[N],d[N]; int sum[N*100],ls[N*100],rs[N*100],root[N],siz,ps[N],vis[N]; int pl[50],pr[50],aa,bb; int vl[N],vr[N]; 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 */