[洛谷P6859]蝴蝶与花

it2023-11-01  70

题目

传送门 to luogu

思路

这思路真是绝了……我不知道该怎么说。

注意到左端点需要最小,所以我们一来先猜 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,r1] 的和一定是 k − 1 k-1 k1 。故 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 k1=i=lr1ai 时,可以分成三种情况:

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; }
最新回复(0)