Codeforces 341D Vessels 线段树 OR Set

题意:n个碟子,第i个碟子能装a[i]水,若第i个碟子装满,则多余的水流到下一层碟子,下一层依次类推
初始碟子为空,m个操作,op1:第x个碟子装p单位水,op2:询问第k个碟子有多少水.n,m<=2e5,a[i]<=1e9;


法1:
第i个碟子装水,二分最远能流到第x个,用线段树将i~x-1置为0.将x置为res-val.(res=sum[i~x])
查询时直接query(x,x) O(nlog^2n) 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+20;
ll a[N];
struct node{
	int l,r;
	int mid()
	{
		return (l+r)>>1;
	}
	ll sum,lz;
}t[N<<2];
void push_up(int rt)
{
	t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
}
void build(int rt,int l,int r)
{
	t[rt].l=l,t[rt].r=r;
	t[rt].lz=-1;
	if(l==r)
	{
		t[rt].sum=a[l]; 
		return; 
	}
	int m=t[rt].mid();
	build(rt<<1,l,m);
	build(rt<<1|1,m+1,r);
	push_up(rt);
}
void push_down(int rt)
{
	if(t[rt].lz!=-1)
	{
		t[rt<<1].lz=t[rt<<1|1].lz=0;
		t[rt<<1].sum=t[rt<<1|1].sum=0;
		t[rt].lz=-1;
	}
}
void update(int rt,int ql,int qr,int val)
{
	int l=t[rt].l,r=t[rt].r;
	if(ql<=l&&qr>=r)
	{
		t[rt].sum=val;
		t[rt].lz=val;
		return;
	}
	push_down(rt);
	int m=t[rt].mid();
	if(ql<=m)
		update(rt<<1,ql,qr,val);
	if(qr>m)
		update(rt<<1|1,ql,qr,val);
	push_up(rt);
}
ll query(int rt,int ql,int qr)
{
	int l=t[rt].l,r=t[rt].r;
	if(ql<=l&&qr>=r)
		return t[rt].sum;
	push_down(rt);
	int m=t[rt].mid();
	ll ans=0;
	if(ql<=m)
		ans+=query(rt<<1,ql,qr);
	if(qr>m)
		ans+=query(rt<<1|1,ql,qr);
	return ans;
}
bool check(int l,int r,ll x)
{
	ll res=query(1,l,r);
	return res>=x;
}
int main()
{
	int n;
	while(cin>>n)
	{
		for(int i=1;i<=n;i++)
			scanf("%I64d",&a[i]);
		//n++,a[n]=2e16;
		build(1,1,n);
		int m;
		scanf("%d",&m);
		while(m--)
		{
			int op,p;
			ll x;
			scanf("%d",&op);	
			if(op==1)
			{
				scanf("%d%I64d",&p,&x);
				ll l=p,r=n,pos=n+1;
				while(l<=r)
				{
					ll mid=(l+r)>>1;
					if(check(p,mid,x))
						r=mid-1,pos=mid;
					else
						l=mid+1;
				}
				ll res;
				if(pos<=n)
					res=query(1,p,pos),update(1,pos,pos,res-x);
				if(pos>p)
					update(1,p,pos-1,0);	
			}
			else
			{
				scanf("%I64d",&x);
				printf("%I64d\n",a[x]-query(1,x,x));
			}
		}	
	}
	return 0;
}

法2:
用set存当前还能装水的碟子下标,第i个碟子装水时 直接模拟.二分找到下一个下标,若不能装满 则结束.

若下一个被装满 在set中删除该下标 继续找下一个,该步骤总共不会超过nlogn次

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+20;
int a[N],b[N];
set<int> s;
int main()
{
	int n,m;
	while(cin>>n)
	{
		s.clear();
		memset(b,0,sizeof(b));
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]),s.insert(i);
		cin>>m;
		while(m--)
		{
			int op,pos,x;
			set<int>::iterator it;
			scanf("%d",&op);
			if(op==1)
			{
				scanf("%d%d",&pos,&x);
				while(x)
				{
					it=s.lower_bound(pos);
					if(it==s.end())
						break;
					pos=*it;
					if(b[pos]+x>a[pos])
					{
						x-=a[pos]-b[pos];
						b[pos]=a[pos];
						s.erase(it);
					}	
					else
					{
						b[pos]+=x;
						break;
					}
				}		
			}
			else
			{
				scanf("%d",&pos);
				printf("%d\n",b[pos]);
			}
		}
	}
	return 0;
}









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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值