BZOJ4504 K个串

Description

兔子们在玩k个串的游戏。首先,它们拿出了一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次)。
兔子们想知道,在这个数字序列所有连续的子串中,按照以上方式统计其所有数字之和,第k大的和是多少。

Input

第一行,两个整数n和k,分别表示长度为n的数字序列和想要统计的第k大的和
接下里一行n个数a_i,表示这个数字序列

Output

一行一个整数,表示第k大的和

Sample Input

7 5
3 -2 1 2 2 1 3 -2

Sample Output

4

HINT

1 <= n <= 100000, 1 <= k <= 200000, 0 <= |a_i| <= 10^9数据保证存在第 k 大的和

题解:

       建出主席树。插入一个节点r时会对[pre[a[i]+1,r]内节点产生贡献。对于每个r,将权值最大的区间[l,r]加入优先队列中。将一个区间弹出后,设最大值取在m位置,则将[l,m-1]和[m+1,r]分别加入,重复k次即可。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+10;
const LL inf=0x3f3f3f3f3f3f3f3f;

int n,k;
map<int,int> Map;
struct Statu {
	LL val;
	int x,l,r,ord;
	bool operator < (const Statu &other) const {
		return val<other.val;
	}
}now;
priority_queue<Statu> Q;

namespace ChairmanTree {
	#define ls tree[x].lch
	#define rs tree[x].rch
	
	int root[N],tot;
	struct Node {
		int lch,rch;
		LL tag;
		pair<LL,int> P;
	}tree[N*40];

	inline int _new(int x) {
		tree[++tot]=tree[x];
		return tot;
	}

	inline void Pushdown(int x) {
		if (!tree[x].tag) return;
		ls=_new(ls); tree[ls].tag+=tree[x].tag; tree[ls].P.first+=tree[x].tag;
		rs=_new(rs); tree[rs].tag+=tree[x].tag; tree[rs].P.first+=tree[x].tag;
		tree[x].tag=0;
	}

	int Build(int l,int r) {
		int x=_new(0);
		tree[x].P=make_pair(0,l);
		if (l==r) return x;
		int mid=(l+r)>>1;
		ls=Build(l,mid); rs=Build(mid+1,r);
		return x;
	}

	int Updata(int l,int r,int lt,int rt,int val,int y) {
		if (l>=lt&&r<=rt) {
			int x=_new(y);
			tree[x].tag+=val; tree[x].P.first+=val; return x;
		}
		Pushdown(y);
		int x=_new(y),mid=(l+r)>>1;
		if (lt<=mid) ls=Updata(l,mid,lt,rt,val,tree[y].lch);
		if (mid<rt) rs=Updata(mid+1,r,lt,rt,val,tree[y].rch);
		tree[x].P=max(tree[ls].P,tree[rs].P);
		return x;
	}
	
	pair<LL,int> Query(int x,int l,int r,int lt,int rt) {
		if (l>=lt&&r<=rt) return tree[x].P;
		Pushdown(x);
		int mid=(l+r)>>1; pair<LL,int> ans=make_pair(-inf,0);
		if (lt<=mid) ans=max(ans,Query(ls,l,mid,lt,rt));
		if (mid<rt) ans=max(ans,Query(rs,mid+1,r,lt,rt));
		return ans;
	}
	
	void Extend(int x,int l,int r) {
		if (l<=r) {
			pair<LL,int> ans=Query(x,1,n,l,r);
			Q.push((Statu){ans.first,x,l,r,ans.second});
		}
	}
}
using namespace ChairmanTree;

int main() {
	scanf("%d %d",&n,&k);
	root[0]=Build(1,n);
	for (int i=1;i<=n;i++) {
		LL x; scanf("%lld",&x);
		root[i]=Updata(1,n,Map[x]+1,i,x,root[i-1]);
		Map[x]=i; Extend(root[i],1,i);
	}
	for (int i=1;i<=k;i++) {
		now=Q.top(); Q.pop();
		Extend(now.x,now.l,now.ord-1); Extend(now.x,now.ord+1,now.r);
	}
	printf("%lld",now.val);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值