D. Petya and Array(權值線段樹or樹狀數組),模仿逆序對

知識點:對於求逆序對的方法的擴展。
https://codeforces.com/problemset/problem/1042/D
題目:
給定一個序列,要求得到區間和小於x的總序列數
在這裏插入圖片描述

在這裏插入圖片描述

方法:和求逆序數的方法差不多。sum[i]sum[j]<k=>sum[j]>sum[i]ksum[i]-sum[j]<k =>sum[j]>sum[i]-k
所以只需要求 i>j同時 滿足上述條件的 <i,j>對。思路挺清晰的。但是難點在於考慮一些細節和座標的離散化。
1 細節:考慮 前綴和的方法來做, 那麼必須在左側置一個0(左閉右開的完備性)。 比如 3 0 -1 2 3 中,-1一個數就可以 是一個結果。所以必須要 -1前必須要有一個前綴和 來做sum[j]。
2 BIT是必須從1開始的。所以代碼用了很好的思想,整體往右移動了一位。
代碼:
1 BIT 2 權值線段樹(但是整體代碼一樣)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
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:"<<i<<"   "<<"計算的和:"<<i-query(1,1,rank,1,n+1)-1<<endl;
		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:"<<pos<<endl;
		//tree.add(pos,1);
		update(1,1,n+1,pos);

	}
	printf("%lld\n",ans);
   return 0;
 }
#include<bits/stdc++.h>



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:"<<i<<"   "<<"計算的和:"<<i-tree.getsum(rank)-1<<endl;
		ans+=i-tree.getsum(rank)-1;

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

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

	}

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

 }

附贈一個 權值線段樹的小題。
求逆序對, 但是這個數組可以 右轉。所以還要遍歷一遍。
http://acm.hdu.edu.cn/showproblem.php?pid=1394

#include <iostream>
#include<cstdio>
#include <cstring>
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<<a[i]<<"??"<<i<<endl;
         //update(1,1,m,a[i]+1);
         //cout<<"xiaoxingxing"<<endl;
         ans+=query(1,a[i]+1,m,1,m);
          update(1,1,m,a[i]+1);
         //cout<<ans<<endl;
     }
     int min1=ans;
     //cout<<ans<<endl;
     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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章