Codeforces 1042 D. Petya and Array

题目地址: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 这篇讲的十分清晰

猜你喜欢

转载自blog.csdn.net/weixin_43248813/article/details/82790379