题解:CF2173F Isla's Memory Thresholds
显然,对于每个询问,我们可以想到一种比较暴力的方法:不断地二分或倍增地查找下一个清空记忆的节点。
由于
当或每次清空的区间长度都极短时,使用二分回答单次询问的时间复杂度为
设每次清空的区间长度为
假设当前枚举到
那么:假设最右端的长度为
因此,对于每个
显然,
因此,通过此法,我们可以倍增地找到所有的
因此,对于长度不大于
代码如下
#include<bits/stdc++.h>
using namespace std;
#define int long long
int const N = 150007;
int n,q;
int a[N];
long long sum[N];
void solve(){
cin >> n >> q;
for(int i=1 ;i<=n ;i++)
cin >> a[i];
for(int i=1 ;i<=n ;i++)
sum[i] = sum[i-1]+a[i];
while(q--){
int l,r,x;
cin >> l >> r >> x;
int sqrtn = sqrt(n);
int cnt = 0;
int now = l-1;
//找到l~now中每一段都>=x 且 len<=sqrtn 的最大值now
for(int len=1 ;len<=sqrtn ;len++){ //从len==1开始,跳过len==len_i的片段
if(now+len>r) break;
int nextn = now+len;
if(sum[nextn]-sum[now]<x) continue; //len至少更大
//倍增跳过所有长度为len的区间
int step = 1;
for(;nextn+step<=r ;step*=2){
if(sum[nextn+step]-sum[nextn+step-len]<x)
break;
nextn += step;
}
for(;step ;step>>=1){
if(nextn+step<=r and sum[nextn+step]-sum[nextn+step-len]>=x)
nextn += step;
}
//累积答案
cnt += (nextn-now)/len;
now += (nextn-now)/len*len;
}
//暴力处理
while(now!=r){
if(sum[r]-sum[now]<x){
break;
}
int L=now,R=r;
while(L<R){
int mid = (L+R)>>1;
if(sum[mid]-sum[now]>=x)
R = mid;
else
L = mid+1;
}
now = R;
++cnt;
}
cout << cnt << " " << sum[r]-sum[now] << "\n";
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int T = 1;
cin >> T;
while(T--) solve();
}