正题
P6859
题目大意
给你一个由1,2组成的序列,要求完成写下列操作:
- 修改一个数
- 查找一段区间,使其区间和为s(输出左端点最小的一组)
解题思路
用线段树来维护该序列,对于每次查询,先找到从1开始不小于s的一段
如果这一段是s直接输出,否则是s+1
设当前左右端点分别为l,r,考虑向右移动左端点:
- al=1a_l=1al=1,则左端点向右移一位,可以直接满足条件
- al=2,ar+1=1a_l=2,a_{r+1}=1al=2,ar+1=1,则左右端点各向后移一位,可以直接满足条件
- al=ar+1=2a_l=a_{r+1}=2al=ar+1=2,则左右端点各向后移一位,重复进行该操作
上述过程就是在左右端点右边找到第一个1,这可以在线段树上二分实现
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 2000100
using namespace std;
ll n,m,x,y,g1,g2,sum,now,a[N];
char s[10];
struct Tree
{#define ls x*2#define rs x*2+1ll s[N<<2];void push_up(ll x){s[x]=s[ls]+s[rs];return;}void build(ll x,ll l,ll r){if(l==r){s[x]=a[l];return;}ll mid=l+r>>1;build(ls,l,mid);build(rs,mid+1,r);push_up(x);return;}void change(ll x,ll l,ll r,ll y){if(l==r){s[x]=a[l];return;}ll mid=l+r>>1;if(y<=mid)change(ls,l,mid,y);else change(rs,mid+1,r,y);push_up(x);return;}ll ask(ll x,ll l,ll r,ll k){if(l==r){now+=s[x];return l;}ll mid=l+r>>1;if(k>s[ls]){now+=s[ls];return ask(rs,mid+1,r,k-s[ls]);}else return ask(ls,l,mid,k);}ll ask1(ll x,ll l,ll r,ll y,ll k){if(l==r)return l;ll mid=l+r>>1;if(y>mid)return ask1(rs,mid+1,r,y,k-s[ls]);else if(s[ls]-k==(mid-y+1)*2)return ask1(rs,mid+1,r,mid+1,0);else return ask1(ls,l,mid,y,k);}
}T;
int main()
{scanf("%lld%lld",&n,&m);for(ll i=1;i<=n;++i){scanf("%lld",&a[i]);sum+=a[i];}T.build(1,1,n);while(m--){scanf("%s",s);if(s[0]=='C'){scanf("%lld%lld",&x,&y);sum+=y-a[x];a[x]=y;T.change(1,1,n,x);}else{scanf("%d",&y);if(!y||y>sum){puts("none");continue;}else if(y==1){x=T.ask1(1,1,n,1,0);if(a[x]==1)printf("%d %d\n",x,x);else puts("none");continue;}now=0;x=T.ask(1,1,n,y);if(now==y){printf("%lld %lld\n",1,x);continue;}g1=g2=n+1;if(now!=x*2)g1=T.ask1(1,1,n,1,0);if(x<n&&sum-now!=(n-x)*2)g2=T.ask1(1,1,n,x+1,now);if(x+g1-1>n&&g2>n)puts("none");else if((g1+1<=g2-x+1||g2>n)&&x+g1-1<=n)printf("%lld %lld\n",g1+1,x+g1-1);else printf("%lld %lld\n",g2-x+1,g2);}}return 0;
}