知识点:对于求逆序对的方法的扩展。
https://codeforces.com/problemset/problem/1042/D
题目:
给定一个序列,要求得到区间和小于x的总序列数
方法:和求逆序数的方法差不多。 s u m [ i ] − s u m [ j ] < k = > s u m [ j ] > s u m [ i ] − k sum[i]-sum[j]
所以只需要求 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;
}