D. Petya and Array(巧妙地树状数组)

感觉树状数组比较好理解,但确实非常巧妙

s u m n [ i ] i 设sumn[i]为前i个数的和

1 n 思路就是枚举1-n每个数作为结尾

s u m n [ i ] t , 找每个数前面前缀大于sumn[i]-t的前缀有多少,大于就符合要求

c n t , 那么我们直接开一个cnt数组保存前缀,从小到大排序

s u m n [ i ] , s u m n [ i ] t 对于当前枚举的sumn[i],二分出大于sumn[i]-t的至少是第几大大前缀

然后看一下大于这个排名的有多少已经被填充了

, s u m n [ i ] , 计算完这个答案后,二分一下sumn[i]是第几名前缀,填充到树状数组种

为以后计算答案做贡献

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
ll n,t,a[maxn],cnt[maxn],sumn[maxn];
class binary_tree
{
	private:
		int sumn[maxn];
		int lowbit(int x){return x&(-x);}
	public:
		void add(int x){
			for(int i=x;i<=n+1;i+=lowbit(i))	sumn[i]++;	
		}
		ll get(int x){
			ll ans=0;
			for(int i=x;i;i-=lowbit(i))	ans+=sumn[i];
			return ans;
		}
}bit;
int main()
{
	cin >> n >> t;
	for(int i=2;i<=n+1;i++)//从下标2起使树状数组下标不会为0 
	{
		cin >> a[i];
		sumn[i] = sumn[i-1]+a[i];
		cnt[i]=sumn[i];
	}
	sort(cnt+1,cnt+n+2);//为什么包括cnt[1]?因为sumn[1]什么都不选也是一种前缀 
	ll ans=0;
	for(int i=1;i<=n+1;i++)
	{
		int pos = upper_bound(cnt+1,cnt+n+2,sumn[i]-t)-cnt;//第一个大的是第几名 
		ans+=(i-1-bit.get(pos-1));//i-1个区间,去掉不满足的get(pos-1)
		pos=lower_bound(cnt+1,cnt+2+n,sumn[i]) - cnt ;//填自己原来的位置去 
		bit.add(pos);
	}
	cout<<ans;
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/106856193
今日推荐