题目地址:http://codeforces.com/contest/1042/problem/D
题意:给出n个数字,求出区间和小于t的数量有多少
/*
1042D 本质是求比t小的区间和
因此类比求区间第几大
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug puts("-----")
#define pi (acos(-1.0))
#define eps (1e-8)
#define inf (1<<30)
using namespace std;
const int N = 2e5+100;
typedef long long ll;
vector<ll> V;
int tree[N<<3]; //小心越界
ll presum[N]; //储存前缀和
ll a[N];
ll n, t, ans ,m;
int get_id(ll num)
{
return lower_bound(V.begin(),V.end(),num)-V.begin()+1;
}
void buildTree(int rt , int l ,int r )
{
tree[rt]=0;
if(l==r)return;
int mid = (l+r)>>1;
//这句话写错了 传入的左边界一直是1 难怪一直re 查了两小时。。。以后再也不用 l 当左边界了 学到了
// buildTree(rt<<1,1,mid);
buildTree(rt<<1,l,mid);
buildTree(rt<<1|1,mid+1,r);
}
void Updata(int pos, int rt, int l, int r) //将位置为pos的 从根节点向下插入 到下标为rt的位置
{
tree[rt]+=1;
if(l==r)return;
int mid = (l+r)>>1;
if(pos<=mid) Updata(pos,rt<<1,l,mid);
else Updata(pos,rt<<1|1,mid+1,r);
}
void query(int pos , int rt, int l ,int r)
{
if(l==r)return;
int mid = (l+r)>>1;
if(pos<=mid)
query(pos,rt<<1,l,mid);
else
{
ans+=tree[rt<<1]; // 因为pos 为a = presum[i]+t 所以要寻找的是 presum[k]> a,因此右节点就是我们要求的, rt<<1画树之后可以理解
query(pos,rt<<1|1,mid+1,r);
}
}
int main()
{
cin>>n>>t;
// scanf("%lld%lld",&n,&t);
for (int i = 1; i <= n; ++i)
{
cin>>a[i];
// scanf("%lld",&a[i]);
presum[i]=presum[i-1]+a[i];
V.push_back(presum[i]);
}
if(n==1)
{
if(a[1]<t)
// printf("1\n");
cout<<"1"<<endl;
else
// printf("0\n");
cout<<"0"<<endl;
return 0;
}
for(int i = 1;i<=n;i++)
{
m = presum[i]+t;
V.push_back(m);
}
sort(V.begin(),V.end());
V.erase(unique(V.begin(), V.end()),V.end()); // 离散化去重
// int len = unique(V.begin(),V.end())-V.begin();
int len = V.size();
buildTree(1,1,len);
Updata(get_id(presum[n]),1,1,len);
// Updata(get_id(presum[n]),1,1,1,len);
for(int i = n-1; i>=0; i--)
{ //>=0是因为最左侧还剩下一个presum[1]要检测 我在这里查了很久
m = presum[i]+t;
query(get_id(m),1,1,len);//查询
if(i>0)
Updata(get_id(presum[i]),1,1,len);
// Updata(get_id(presum[i]),1,1,1,len);
}
cout<<ans<<endl;
// printf("%lld\n",res);
}
第一次写权值线段树,参考了https://www.cnblogs.com/a249189046/p/9664895.html 的代码
对于线段树理解 学习了https://blog.csdn.net/zearot/article/details/48299459 这篇讲的十分清晰