P4145 上帝造题的七分钟2 / 花神游历各国 题解
Description
"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。
SimpleInput
第一行一个整数 \(n\),代表数列中数的个数。
第二行 \(n\) 个正整数,表示初始状态下数列中的数。
第三行一个整数 \(m\),表示有 \(m\) 次操作。
接下来 \(m\) 行每行三个整数k,l,r,
k=0表示给 \([l,r]\) 中的每个数开平方(下取整)k=1表示询问 \([l,r]\) 中各个数的和。
数据中有可能 \(l>r\),所以遇到这种情况请交换 \(l\) 和 \(r\)。
10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8
SimpleOutput
对于询问操作,每行输出一个回答。
19
7
6
数据范围
对于 \(30\%\) 的数据 \(1\le n,m\le 10^3\) 数列中的数不超过 32767。
对于 \(100\%\) 的数据 \(1\le n,m\le 10^5\) 数列中的数不超过 \(10^{12}\)。
Solution
对于这道题目,我们考虑 分块。
Q1:为什么不会出现鬼才TLE
A1:因为对于极限数据 \(10^{12}\) 开 6 次平方根之后就会变成 1,所以只有大概 6 倍的常数,适当卡常就可以通过
Q2:如何分块?
A2:暴力即可
由于过于鬼畜,所以直接放代码。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 100009;
long long a[N],sum[N];
int l[N],r[N],belong[N];
bool visit[N];
int len,n,q,opt,L,R;
template <class T> inline void read(T &x) {
x = 0;
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
}
inline void modify(int L,int R) {
if(belong[L] == belong[R]) {
if(visit[belong[L]]) return;
for(register int i = L;i <= R;++i) {
sum[belong[L]] -= a[i];
a[i] = sqrt(a[i]);
sum[belong[L]] += a[i];
}
if(sum[belong[L]] == r[belong[L]] - l[belong[L]] + 1) visit[belong[L]] = 1;
}else {
for(register int i = belong[L] + 1;i < belong[R];++i) {
if(visit[i]) continue;
for(register int j = l[i];j <= r[i];++j) {
sum[i] -= a[j];
a[j] = sqrt(a[j]);
sum[i] += a[j];
}
if(sum[i] == r[i] - l[i] + 1) visit[i] = 1;
}
for(register int i = L;i <= r[belong[L]];++i) {
sum[belong[L]] -= a[i];
a[i] = sqrt(a[i]);
sum[belong[L]] += a[i];
}
for(register int i = l[belong[R]];i <= R;++i) {
sum[belong[R]] -= a[i];
a[i] = sqrt(a[i]);
sum[belong[R]] += a[i];
}
if(sum[belong[L]] == r[belong[L]] - l[belong[L]] + 1) visit[belong[L]] = 1;
if(sum[belong[R]] == r[belong[R]] - l[belong[L]] + 1) visit[belong[R]] = 1;
}
}
inline long long query(int L,int R) {
long long ans = 0;
if(belong[L] == belong[R]) {
for(register int i = L;i <= R;++i) ans = ans + a[i];
}else {
for(register int i = belong[L] + 1;i < belong[R];++i) {
ans = ans + sum[i];
}
for(register int i = L;i <= r[belong[L]];++i) ans += a[i];
for(register int i = l[belong[R]];i <= R;++i) ans += a[i];
}
return ans;
}
int main() {
read(n); len = sqrt(n);
for(register int i = 1;i <= n;++i) {
read(a[i]);
belong[i] = i / len;
sum[belong[i]] += a[i];
if(!l[belong[i]]) l[belong[i]] = i;
r[belong[i]] = i;
}
read(q);
while(q--) {
read(opt); read(L); read(R);
if(L > R) swap(L,R);
if(opt == 0) {
modify(L,R);
}else {
printf("%lld\n",query(L,R));
}
}
return 0;
}
其实当时交上去的时候,我还被卡常了 50pts,最后加了一点玄学才过,果然是人傻常数大。

浙公网安备 33010602011771号