转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
题目:给出个序列,问有多少个二元组(i,j)满足a1a2...alarar + 1... an 逆序对数不超过K
显然的一个问题是如果(i,j)满足,那么(i,j+r) r>=0肯定满足
所以枚举左端点,维护右端点,典型的two points。
然后就是更新整个区间的逆序对数了。
删除一个数,或者添加一个数需要得增加或者减少的逆序对数,最好有当时的序列情况。
典型的就是可持久化线段树。
排序离散化后,从前往后,以及从后往前分别建立主席树,就是维护前缀和后缀。
然后就是two points过程了。更新的时候注意细节就行了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define lowbit(i) (i&(-i))
#define LL long long
using namespace std;
const int N=100005;
const int M=5000000;
int n,m,a[N];
LL k;
vector<int>v;
struct persistent_tree{
int lson[M],rson[M],c[M],T[M];
int tot,m;
void Init(int t,int _m){
tot=0;m=_m;
T[t]=bulid(1,m);
}
int bulid(int l,int r){
int root=tot++;
c[root]=0;
if(l!=r){
int m=(l+r)>>1;
lson[root]=bulid(l,m);
rson[root]=bulid(m+1,r);
}
return root;
}
int update(int root,int pos,int val){
int newroot=tot++,tmp=newroot;
c[newroot]=c[root]+val;
int l=1,r=m;
while(l<r){
int mid=(l+r)>>1;
if(pos<=mid){
rson[newroot]=rson[root];
lson[newroot]=tot++;
newroot=lson[newroot];
root=lson[root];
r=mid;
}
else{
lson[newroot]=lson[root];
rson[newroot]=tot++;
newroot=rson[newroot];
root=rson[root];
l=mid+1;
}
c[newroot]=c[root]+val;
}
return tmp;
}
int query(int root,int l,int r,int L,int R){
if(R<L) return 0;
if(l==L&&r==R)
return c[root];
int m=(l+r)>>1;
if(R<=m) return query(lson[root],l,m,L,R);
else if(L>m) return query(rson[root],m+1,r,L,R);
else return query(lson[root],l,m,L,m)+query(rson[root],m+1,r,m+1,R);
}
void insert(int now,int old,int pos,int val){
T[now]=update(T[old],pos,val);
}
}pre,suf;
int main(){
cin>>n>>k;
v.push_back(-1);
for(int i=1;i<=n;i++){
cin>>a[i];
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.resize(unique(v.begin(),v.end())-v.begin());
for(int i=1;i<=n;i++)
a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin();
m=v.size()-1;
pre.Init(0,m);
suf.Init(m+1,m);
for(int i=1;i<=n;i++)
pre.insert(i,i-1,a[i],1);
for(int i=n;i>=1;i--)
suf.insert(i,i+1,a[i],1);
LL cur=0,ans=0;
for(int i=1;i<=n;i++)
cur+=(LL)pre.query(pre.T[i],1,m,a[i]+1,m);
//cur为初始状态下的逆序对数
for(int i=1,j=2;i<=n;i++){
while(j<=n&&cur>k){
//将j点删除,便是找到[1,i]中比j大的,[j+1]中比j小的
cur-=(LL)pre.query(pre.T[i],1,m,a[j]+1,m);
cur-=(LL)suf.query(suf.T[j],1,m,1,a[j]-1);
j++;
}
ans+=n-j+1;
if(j==i+1){
//始终保持j>i,删除j
cur-=(LL)pre.query(pre.T[i],1,m,a[j]+1,m);
cur-=(LL)suf.query(suf.T[j],1,m,1,a[j]-1);
j++;
}
//恢复i+1
//便是找到[1,i]中比i+1大的,[j,m]中比i+1小的
cur+=(LL)pre.query(pre.T[i],1,m,a[i+1]+1,m);
cur+=(LL)suf.query(suf.T[j],1,m,1,a[i+1]-1);
}
cout<<ans<<endl;
return 0;
}