P4145 上帝造题的七分钟2 / 花神游历各国 题解
简要题意:
给定一个长为 \(n\) 的序列 \(a\),\(q\) 次操作:
- 对 \([l,r]\) 区间进行开平方操作。即 \(a_i \gets \lfloor \sqrt{a_i} \rfloor (i \in [l,r])\)..
- 询问 \([l,r]\) 区间的和。即 \(a_{i=l}^r\) 的和。
\(n,q \leq 10^5\), \(1 \leq a_i \leq 10^{12}\)
我们注意到一个性质:
开平方操作数将会很快变成 \(1\). 即使是 \(10^{12}\),在 六次开平方操作之后 也变成了 \(1\).
所以修改操作 有效的次数 很少,我们最多只需要对每个数进行 \(6\) 次修改,其余的情况就可以忽略不计。
我们可以维护前缀和 \(s\),暴力维护!但如果 \(a_{i=l}^r a_i = r - l + 1\),说明 \(a_i = 1 (i \in [l,r])\),那么可以跳过这个操作。
时间复杂度:\(\mathcal{O}(n)\).
实际得分:\(100pts\).
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+1;
inline ll read(){char ch=getchar(); int f=1; while(!isdigit(ch)) {if(ch=='-') f=-f; ch=getchar();}
ll x=0;while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f;}
int n,q;
ll a[N],s[N];
int main() {
n=read();
for(int i=1;i<=n;i++) a[i]=read(),s[i]=s[i-1]+a[i];
q=read(); while(q--) {
int op=read(),l=read(),r=read();
if(l>r) swap(l,r); //细节
if(!op) {
if(s[r]-s[l-1]==r-l+1) continue;
else
for(int i=l;i<=n;i++) {
if(i<=r) a[i]=sqrt(a[i]);
s[i]=s[i-1]+a[i];
} //暴力维护
} else printf("%lld\n",s[r]-s[l-1]);
}
return 0;
}
后记
这个题可以用线段树等维护前缀和 \(s\),还可以大力线段树。不过,本来修改只有 \(6\) 次有效,暴力也不怕!是不是!
简易的代码胜过复杂的说教。