取石子 - 线段树

题目大意:
有两个人,一开始分别有x和y个石子。两个人轮流操作n轮,第i轮从对面那里拿来a_i个石子,若不足则全拿。m次修改x或y或a_i然后问最后第一个人手上有几个石子。 n , m ≤ 5 × 1 0 5 n,m\le5\times10^5 n,m5×105
题解:
显然就是每次x加或者减一个数字对s取min对0取max。
考虑分治,设solve(L,R,x)表示做完L,R的操作,x会变成多少。首先考虑(mid,R]这个区间中最大值和最小值的差,如果大于等于s,那么无论左区间做完了是啥,都不会对右区间有影响,因此直接return solve(mid+1,R,rand()%s)即可。
否则在右区间只会碰到一侧边界(不可能同时到达上边界和下边界)。发现可以手算,因此只递归左区间即可。
然后这玩意拿个线段树维护即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsinged lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
    const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;inline int gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
    inline int inn() { int x,ch;while((ch=gc())<'0'||ch>'9');x=ch^'0';while((ch=gc())>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; }
}using INPUT_SPACE::inn;
namespace OUTPUT_SPACE{
    char ss[500000*15],tt[20];int ssl,ttl;inline int Flush() { return fwrite(ss+1,sizeof(char),ssl,stdout),ssl=0,0; }
    inline int print(int x) { if(!x) ss[++ssl]='0';for(ttl=0;x;x/=10) tt[++ttl]=char(x%10+'0');for(;ttl;ttl--) ss[++ssl]=tt[ttl];return ss[++ssl]='\n'; }
}using OUTPUT_SPACE::print;using OUTPUT_SPACE::Flush;
const int N=500010;int a[N];
struct segment{ int l,r;lint mx,mn,s;segment *ch[2]; }*rt;
inline int _init(segment* &rt,int v) { if(v>=0) rt->mx=v,rt->mn=0;else rt->mx=0,rt->mn=v;return rt->s=v,0; }
inline int push_up(segment* &rt)
{
    rt->mn=min(rt->ch[0]->mn,rt->ch[0]->s+rt->ch[1]->mn);
    rt->mx=max(rt->ch[0]->mx,rt->ch[0]->s+rt->ch[1]->mx);
    return rt->s=rt->ch[0]->s+rt->ch[1]->s,0;
}
inline int build(segment* &rt,int l,int r)
{
    rt=new segment,rt->l=l,rt->r=r;int mid=(l+r)>>1;
    if(l==r) return _init(rt,a[l]),0;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),push_up(rt);
}
inline int update(segment* &rt,int p,int v)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(l==r) return _init(rt,a[l]=v),0;
    return update(rt->ch[p>mid],p,v),push_up(rt);
}
inline int query(segment* &rt,int x,int s)
{
    int l=rt->l,r=rt->r;if(l==r) return max(min(x+a[l],s),0);
    lint rmx=rt->ch[1]->mx,rmn=rt->ch[1]->mn;
    if(rmx-rmn>=s) return query(rt->ch[1],0,s);
    return int(min(max((lint)query(rt->ch[0],x,s),-rmn),s-rmx)+rt->ch[1]->s);
}
int main()
{
    int n=inn(),q=inn(),x=inn(),y=inn();
    rep(i,1,n) a[i]=((i&1)?inn():-inn());
    build(rt,1,n);
    while(q--)
    {
        int tp=inn(),p;
        if(tp==1) x=inn();else if(tp==2) y=inn();
        else p=inn(),update(rt,p,(p&1)?inn():-inn());
        print(query(rt,x,x+y));
    }
    return Flush(),0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值