D. Petya and Array(权值线段树or树状数组),模仿逆序对

知识点:对于求逆序对的方法的扩展。
https://codeforces.com/problemset/problem/1042/D
题目:
给定一个序列,要求得到区间和小于x的总序列数
D. Petya and Array(权值线段树or树状数组),模仿逆序对_第1张图片

D. Petya and Array(权值线段树or树状数组),模仿逆序对_第2张图片

方法:和求逆序数的方法差不多。 s u m [ i ] − s u m [ j ] < k = > s u m [ j ] > s u m [ i ] − k sum[i]-sum[j]sum[j]>sum[i]-k sum[i]sum[j]<k=>sum[j]>sum[i]k
所以只需要求 i>j同时 满足上述条件的 对。思路挺清晰的。但是难点在于考虑一些细节和坐标的离散化。
1 细节:考虑 前缀和的方法来做, 那么必须在左侧置一个0(左闭右开的完备性)。 比如 3 0 -1 2 3 中,-1一个数就可以 是一个结果。所以必须要 -1前必须要有一个前缀和 来做sum[j]。
2 BIT是必须从1开始的。所以代码用了很好的思想,整体往右移动了一位。
代码:
1 BIT 2 权值线段树(但是整体代码一样)

#include 
#include 
#include 
#include 
using namespace std;
const int maxn=2e5+2;
typedef long long ll;
int tree[maxn*4];

int n;
ll a[maxn],b[maxn],t;
void build(int index,int l,int r){
     if(l==r){
        tree[index]=0;return;
     }
     int mid=(l+r)>>1;
     tree[index]=0;
     build(index<<1,l,mid);
     build(index<<1|1,mid+1,r);
     return ;
}
void update(int index,int l,int r,int pos){
     if(l==r&&r==pos){
        tree[index]++;
        return ;
     }
     int mid=(l+r)>>1;
     if(pos<=mid)update(index<<1,l,mid,pos);
     if(pos>mid)update(index<<1|1,mid+1,r,pos);
     tree[index]=tree[index<<1]+tree[index<<1|1];
     return;
}
int query(int index,int ql,int qr,int l,int r){
    if(ql>qr)return 0;
    if(ql<=l&&qr>=r){
        return tree[index];
    }
    int mid=(l+r)>>1;
    int ans=0;
    if(ql<=mid)ans+=query(index<<1,ql,qr,l,mid);
    if(qr>mid)ans+=query(index<<1|1,ql,qr,mid+1,r);

    return ans;
}
int main()

{



	scanf("%d %lld",&n,&t);
    build(1,1,n+1);
	for(int i=2;i<=n+1;i++) {

		scanf("%lld",&a[i]);

		a[i]+=a[i-1];

		b[i]=a[i];

	}

	sort(b+1,b+n+2);ll ans=0;
    //puts("?????");
    int pos=lower_bound(b+1,b+n+2,0)-b;
    update(1,1,n+1,pos);
	for(int i=2;i<=n+1;i++){
		int rank=upper_bound(b+1,b+n+2,a[i]-t)-b-1;//在b+1上二分,当然这样写了。
        //cout<<"i:"<
		ans+=i-query(1,1,rank,1,n+1)-1;//之所以-1,因为 【i,i】不可用
		int pos=lower_bound(b+1,b+n+2,a[i])-b;//真正的下标,为了BIT
        //cout<<"pos:"<
		//tree.add(pos,1);
		update(1,1,n+1,pos);

	}
	printf("%lld\n",ans);
   return 0;
 }
#include



using namespace std;

typedef long long ll;

const int maxn=200005;

int n,sum[maxn];

ll a[maxn],b[maxn],t;



struct bit{

	int lowbit(int x){

		return x&(-x);

	}

	int getsum(int k){

		int res=0;

		while(k>=1){

			res+=sum[k];

			k-=lowbit(k);

		}

		return res;

	}



	void add(int x,int val){

		while(x<=n+1){

			sum[x]+=val;

			x+=lowbit(x);

		}

	}

	void clear(){

		memset(sum,0,sizeof(sum));

	}

};

int main()

{

	bit tree;

	scanf("%d %lld",&n,&t);

	for(int i=2;i<=n+1;i++) {

		scanf("%lld",&a[i]);

		a[i]+=a[i-1];

		b[i]=a[i];

	}

	sort(b+1,b+n+2);ll ans=0;

	for(int i=1;i<=n+1;i++){

		int rank=lower_bound(b+1,b+n+2,a[i]-t)-b;
        //cout<<"i:"<
		ans+=i-tree.getsum(rank)-1;

		int pos=lower_bound(b+1,b+n+2,a[i])-b;

        //cout<<"pos:"<
		tree.add(pos,1);

	}

	printf("%lld\n",ans);

 }

附赠一个 权值线段树的小题。
求逆序对, 但是这个数组可以 右转。所以还要遍历一遍。
http://acm.hdu.edu.cn/showproblem.php?pid=1394

#include 
#include
#include 
using namespace std;
/*
权值线段树
和 可持久化线段树
二维线段树
主席树
权值线段树

进程排序。
*/
const int maxn=5e3+4;
int tree[maxn<<2];
void build(int index,int l,int r){
     if(l==r){
        tree[index]=0;return;
     }
     int mid=(l+r)>>1;
     tree[index]=0;
     build(index<<1,l,mid);
     build(index<<1|1,mid+1,r);
     return ;
}
void update(int index,int l,int r,int pos){
     if(l==r&&r==pos){
        tree[index]++;
        return ;
     }
     int mid=(l+r)>>1;
     if(pos<=mid)update(index<<1,l,mid,pos);
     if(pos>mid)update(index<<1|1,mid+1,r,pos);
     tree[index]=tree[index<<1]+tree[index<<1|1];
     return ;
}
int query(int index,int ql,int qr,int l,int r){
     if(ql<=l&&qr>=r){
        return tree[index];
     }
     int mid=(l+r)>>1;
     int ans=0;
     if(ql<=mid)ans+=query(index<<1,ql,qr,l,mid);
     if(qr>mid)ans+=query(index<<1|1,ql,qr,mid+1,r);
     return ans;
}
int a[maxn];
int main()
{    int m;
     while(~scanf("%d",&m)){
     build(1,1,m);
     //memset(tree,0,sizeof(tree));
     int ans=0;
     for(int i=1;i<=m;i++){
         scanf("%d",&a[i]);
         //cout<
         //update(1,1,m,a[i]+1);
         //cout<<"xiaoxingxing"<
         ans+=query(1,a[i]+1,m,1,m);
          update(1,1,m,a[i]+1);
         //cout<
     }
     int min1=ans;
     //cout<
     for(int i=1;i<=m;i++){
          //min1=min(min1,ans);
          ans=ans+m-2*(a[i]+1)+1;
          min1=min(min1,ans);
     }
     cout<<min1<<endl;
     }







    return 0;
}

你可能感兴趣的:(树状数组,线段树)