知識點:對於求逆序對的方法的擴展。
https://codeforces.com/problemset/problem/1042/D
題目:
給定一個序列,要求得到區間和小於x的總序列數
方法:和求逆序數的方法差不多。
所以只需要求 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;
}