P6859 蝴蝶与花 题解
P6859 蝴蝶与花 题解
题目大意:
给定一个长度为 \(n\) 的序列 \(a_i\) ,\(a_i\in \{ 1,2 \}\) ,每次操作给出两种类型的操作:
- 修改格式:\(C \ i \ val\) 将 \(a_i\) 改为 \(val\in \{1,2\}\)
- 查询格式:\(A \ K\) ,查询整个序列有无一个子串和为 \(K\),输出所有方案中 \(L\) 最小的。
分析:
首先,设要求查询的和为 \(K\) ,首先当 \(K>sum||K=0\) 时是肯定无解的。(此时 \(sum\) 指整个序列的和)
因为要求左端点最小:
很容易想到从 \(1\) 开始,我们先找到以 \(1\) 为左端点,子串和 \(\geq K\) 的最小的右端点记作 \(R\)。
这一点可以通过一个线段树上二分在 \(\Theta(\log n )\) 的时间解决,修改也就是一个简单的单点修改。
此时查询到的子串和一定为 \(K\) 或 \(K+1\) ,这点可以通过反证法证明。
若子段和为 \(K\) 则证明已经找到了答案,着重解决的肯定是子段和为 \(K+1\) 的部分。
为了保证左端点最小,并且保持着当前的子串和为 \(K+1\) 不变,选择两个端点同时向右移动。
在两个端点向右移动的过程中,如果有一个端点的值为 \(1\) ,则找到了答案。
因为它们都从原本的 \(2\) 走到了 \(1\) 。(右端点的值一定为 \(2\))
而左端点可以近似的看作从 \(2\) 走到了 \(1\)。
所以答案就是离左端点最近的 \(1\) ,或者右端点更近的 \(1\)。
用一个 set
维护即可。
Code:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)) f|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=f?-x:x;return;
}
template <typename T>
inline void print(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10^48);return;
}
const int N=2e6+3,INF=1e9;
int n,m,a[N],nxt[N],pre[N];
struct node{
int sum,lazy;
#define sum(x) c[x].sum
#define lazy(x) c[x].lazy
}c[N<<2];
#define lc x<<1
#define rc x<<1|1
inline void Push_up(int x){sum(x)=sum(lc)+sum(rc);return;}
inline void Build(int x,int l,int r){
if(l==r){sum(x)=a[l];return;}
int mid=(l+r)>>1;Build(lc,l,mid),Build(rc,mid+1,r);
Push_up(x);return;
}
inline void Modify(int x,int l,int r,int to,int d){
if(l==r){sum(x)=d;return;}
int mid=(l+r)>>1;if(mid>=to) Modify(lc,l,mid,to,d);
else Modify(rc,mid+1,r,to,d);
Push_up(x);return;
}
int res=0;
inline int Query(int x,int l,int r,int k){
if(l==r){res+=a[l];return l;}
int mid=(l+r)>>1;
if(sum(lc)>=k) return Query(lc,l,mid,k);
else{res+=sum(lc);return Query(rc,mid+1,r,k-sum(lc));}
}
set<int> loc;
int main(){
read(n),read(m);
for(register int i=1;i<=n;++i){read(a[i]);if(a[i]==1) loc.insert(i);}
Build(1,1,n);
while(m--){
char op[2];scanf("%s",op);
if(op[0]=='A'){
int x;read(x);
if(x>sum(1)||x==0){puts("none");continue;}
res=0;int R=Query(1,1,n,x);
if(res==x){print(1),putchar(' '),print(R),putchar('\n');continue;}
int loc1=INF,loc2=INF;
if(!loc.empty()) loc1=*loc.begin();
set<int>::iterator it=loc.lower_bound(R);
if(it!=loc.end()) loc2=*it;
int len=loc2-R+1;
if(loc2==INF&&R+loc1-1>n){puts("none");continue;}
if(loc1>=len) print(len),putchar(' '),print(loc2),putchar('\n');
else print(loc1+1),putchar(' '),print(R+loc1-1),putchar('\n');
}
else{
int i,val;read(i),read(val);
if(a[i]==1) loc.erase(i);
if(val==1) loc.insert(i);
a[i]=val;Modify(1,1,n,i,val);
}
}
return 0;
}