题目
思路
这思路真是绝了……我不知道该怎么说。
注意到左端点需要最小,所以我们一来先猜 l = 1 l=1 l=1 。假如我们求出一个最小的 r r r 使得 [ l , r ] [l,r] [l,r] 中所有数的和超过 k k k ,那么这个和 最多是 k + 1 k+1 k+1 。
同时,如果说 [ l , r ] [l,r] [l,r] 中所有数的和为 k + 1 k+1 k+1 ,那么 [ l , r − 1 ] [l,r-1] [l,r−1] 的和一定是 k − 1 k-1 k−1 。故 a r = 2 a_r=2 ar=2 。此时我们考虑将 l l l 增大一。如果 a l = 1 a_l=1 al=1 那么 [ l + 1 , r ] [l+1,r] [l+1,r] 已经是答案了。否则 [ l + 1 , r + 1 ] [l+1,r+1] [l+1,r+1] 的和是不小于 k k k 的。什么情况下这个和是 k + 1 k+1 k+1 呢?就是 a r + 1 = 2 a_{r+1}=2 ar+1=2 的时候。
可以看到,一个区间 [ l , r ] [l,r] [l,r] 在满足 a r = 2 a_r=2 ar=2 且 k − 1 = ∑ i = l r − 1 a i k-1=\sum_{i=l}^{r-1}a_i k−1=∑i=lr−1ai 时,可以分成三种情况:
- a l = 1 a_l=1 al=1 。此时 [ l + 1 , r ] [l+1,r] [l+1,r] 即为答案。
- a l = 2 a_l=2 al=2 且 a r + 1 = 1 a_{r+1}=1 ar+1=1 。此时 [ l + 1 , r + 1 ] [l+1,r+1] [l+1,r+1] 即为答案。
- a l = a r + 1 = 2 a_l=a_{r+1}=2 al=ar+1=2 。此时令 l ′ = l + 1 , r ′ = r + 1 l'=l+1,\;r'=r+1 l′=l+1,r′=r+1 ,对 [ l ′ , r ′ ] [l',r'] [l′,r′] 递归执行分类。
显然只有一种情况要递归,我们只要能将其加速就好了。这种情况我们只需要问, l / r l/r l/r 开始的连续的 2 2 2 有多少个?可以树状数组(或者线段树)上二分。求出第一个 r r r 同样方法。复杂度 O [ ( n + m ) log n ] \mathcal O[(n+m)\log n] O[(n+m)logn] 。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline int qkpow(int_ b,int_ q,int Mod){
int ans = 1;
for(; q; q>>=1,b=b*b%Mod)
if(q&1) ans = ans*b%Mod;
return ans;
}
const int MaxN = 21;
int bit[1<<MaxN], n, m, a[1<<MaxN];
void modify(int id,int x){
for(int i=id; i<=n; i+=(i&-i))
bit[i] += x;
}
int query(int id){
int res = 0;
for(int i=id; i; i-=(i&-i))
res += bit[i];
return res;
}
/** @return min_{sum(1,x)>=s} x*/
int findS(int s){
int x = 0, now = 0;
for(int j=MaxN-1; j>=0; --j)
if((1<<j)+x <= n
&& bit[x+(1<<j)]+now < s)
now += bit[x += (1<<j)];
return x+1; // x 是严格小于
}
/** @return max_{sum(id,x)=2(x-id+1)} x */
int countTwo(int id){
int x = 0, now = -query(id-1);
for(int j=MaxN-1; j>=0; --j)
if((1<<j)+x <= n) // 没有爆
if((1<<j)+x < id // 不超过 id 那就没事了
|| bit[x+(1<<j)]+now == 2*((1<<j)+x-id+1))
now += bit[x += (1<<j)];
return x;
}
char zhihucuan[1<<10];
int main(){
n = readint(), m = readint();
for(int i=1; i<=n; ++i)
modify(i,a[i] = readint());
while(m --){
scanf("%s",zhihucuan);
char opt = *zhihucuan;
if(opt == 'C'){
int t = readint();
modify(t,-a[t]);
modify(t,a[t] = readint());
}
if(opt == 'A'){
int s = readint();
int r = findS(s);
if(r == n+1 || !s){
puts("none");
continue;
}
if(query(r) == s){
printf("1 %d\n",r);
continue;
}
if(a[1] == 1){ // 去头即可
printf("2 %d\n",r);
continue;
}
int cnt1 = countTwo(1);
int cnt2 = countTwo(r);
if(cnt1 < cnt2-r+1) // 遇到了可以去头的地方!
printf("%d %d\n",cnt1+2,r+cnt1);
else{ // 暴力后移即可
if(cnt2 == n) puts("none");
else printf("%d %d\n",cnt2-r+2,cnt2+1);
}
}
}
return 0;
}