bzoj 4504 - 主席树+区间修改

题目链接:https://darkbzoj.cf/problem/4504

 

解题思路:

题目跟洛谷P2048 差不多,主要差在区间数只能统计一次,洛谷那题区间一样的数可以重复统计.

那么就可以用pre[i]表示i这个数上一次出现的位置在哪,那么对于新的主席树更新区间就是(pre[i],i),之后的操作与洛谷P2048几乎相同

https://blog.csdn.net/a1214034447/article/details/83246731

 

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define lson l,mid
#define rson mid+1,r 
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
const int mod = 1e9+7;
const ll INF = -1e16;
int n,m,root[mx];
int a[mx],rs[mx*30];
int siz,ls[mx*30],d;
ll add[mx*30]; 
struct node
{
	int rt,l,r,p;
	ll c;
	bool operator < (node A)const
	{
		return c < A.c;
	}
};
struct data
{
	int ps;
	ll Ma;
}s[mx*30];
map <int,int> mp;
priority_queue <node> st;
void build(int& rt,int l,int r)
{
	rt = siz++;
	if(l==r){
		s[rt].ps = l;
		return ;
	}
	int mid = (l+r)>>1;
	build(ls[rt],lson);
	build(rs[rt],rson);
}
data up(data a,data b)
{
	if(b.Ma>a.Ma) return b;
	return a;
}
void update(int x,int &y,int l,int r,int L,int R)
{
	y = siz++;
	s[y] = s[x],add[y] = add[x];
	ls[y] = ls[x],rs[y] = rs[x];
	if(L<=l&&r<=R){
		add[y] += d;
		s[y].Ma += d;
		return ;
	}
	int mid = (l+r)>>1;
	if(R<=mid) update(ls[x],ls[y],lson,L,R);
	else if(L>mid) update(rs[x],rs[y],rson,L,R);
	else{
		update(ls[x],ls[y],lson,L,mid);
		update(rs[x],rs[y],rson,mid+1,R);
	}
	s[y] = up(s[ls[y]],s[rs[y]]);
	s[y].Ma += add[y]; 
}
data query(int rt,int l,int r,int L,int R)
{
	if(L<=l&&r<=R) return s[rt];
	int mid = (l+r)>>1;
	data ans = {0,INF};
	if(L<=mid) ans = up(ans,query(ls[rt],lson,L,R));
	if(R>mid) ans = up(ans,query(rs[rt],rson,L,R));
	return data{ans.ps,ans.Ma+add[rt]};
}
int main()
{ 
	scanf("%d%d",&n,&m);
	build(root[0],1,n);
	for(int i=1;i<=n;i++){
		scanf("%d",a+i);
		d = a[i];
		update(root[i-1],root[i],1,n,mp[a[i]]+1,i);
		mp[a[i]] = i;
		data ret = query(root[i],1,n,1,i);
		st.push(node{root[i],1,i,ret.ps,ret.Ma});
	}
	data ret;node now;
	while(m--){
		now = st.top();
		st.pop();
		if(now.p-now.l>0){
			ret = query(now.rt,1,n,now.l,now.p-1);
			st.push(node{now.rt,now.l,now.p-1,ret.ps,ret.Ma});
		}
		if(now.r-now.p>0){
			ret = query(now.rt,1,n,now.p+1,now.r);
			st.push(node{now.rt,now.p+1,now.r,ret.ps,ret.Ma});
		} 
		//cout << now.c << endl; 
	}
	printf("%lld\n",now.c);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值