P4145 上帝造题的七分钟2 / 花神游历各国
P4145 上帝造题的七分钟2 / 花神游历各国
题目
思路
听说直接暴力处理每一次更改+树状数组查询+小优化就能过这道题?!
emm
我是为了练分块才找到这道题的,所以自然是用分块啦
首先,要明白,1e12最多也就是进行6次开平方操作就能变成1(详见下:源自程序计算)
1000000000000
1000000
1000
31
5
2
1
1
我们设a[i][j]
表示原输入数列中的第i个数进行j次开平方后得到的数,因此,当j为0时,就是原来的数列
按老规矩定义int L[max_t] , R[max_t];int pos[nn];
,不会的去分块模板
另外定义int upd[i]
表示第i个块整体开平方的次数
查询
对于每一次查询(不再赘述,直接看代码):
ll query(rr int l , rr int r) {
rr int p = pos[l] , q = pos[r];
rr ll ans = 0;
if(p == q) {//l,r属于同一个块
for(int i = l ; i <= r ; ++i)
ans += a[i][upd[p]];
return ans;
}
for(rr int i = l ; i <= R[p] ; ++i)//开头
ans += a[i][upd[p]];
for(rr int i = p + 1 ; i <= q - 1 ; ++i)//中间
ans += sum[i][upd[i]];
for(rr int i = L[q] ; i <= r ; ++i)//结尾
ans += a[i][upd[q]];
return ans;
}
修改
然后对于每一次修改:
void change_(int l , int r , int p) {//暴力修改第p个块暴力将[l,r]的数开平方
if(upd[p] == 7)return;//小优化,等下讲
for(int i = l ; i <= r ; ++i)
for(int j = 0 ; j <= 6 ; ++j)
a[i][j] = a[i][j + 1];
for(int j = 0 ; j <= 7 ; ++j) {//更新sum[p]
sum[p][j] = 0;
for(int i = L[p] ; i <= R[p] ; ++i)
sum[p][j] += a[i][j];
}
}
void change(int l , int r) {
if(cnt <= 0)return;
int p = pos[l] , q = pos[r];
if(p == q) {//l,r属于同一个块
change_(l , r , p);
return;
}
change_(l , R[p] , p);//开头一段
for(int i = p + 1 ; i <= q - 1 ; ++i)//中间
if(upd[i] < 7)++upd[i],--cnt;
change_(L[q] , r , q);//结尾
}
优化
结果发现,交上去是50分(TLE)
我们的时间效率为 \(O(n\sqrt n)\) 大概在1e7~1e8左右,但是前面带一个"7"的常数,再加上一丢丢小优化理论上是可以过的
先是卡了一波常数,i++
变为++i
,变量尽量带上register
,快速输出,然鹅也没有快多少
考虑到每个数进行6次开方后再开方就无意义了,所以就有了上面见到的优化:if(upd[p] == 7)return;
(在void change_(int l , int r , int p)
中),然后就AC了,学校的老人机也能在200ms的时间下通过单个测试数据(原2000ms)
代码(含对拍)
对拍的命名按照老规矩(见前2篇博客)
AC代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define ll long long
#define nn 100010
#define max_t 1010
#define rr register
//#pragma GCC optimize(3)
using namespace std;
long long read() {
long long re = 0;
char c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0',
c = getchar();
return re;
}
int n , m , t;
ll a[nn][15];
ll sum[max_t][15];
int L[max_t] , R[max_t];
int upd[max_t];
int pos[nn];
int cnt;//cnt也是用于优化,但是已经有"思路"中的优化的情况下效果并不明显(O2优化下总时间快40ms(洛谷))
void Init() {
memset(upd , 0 , sizeof(upd));
for(int i = 1 ; i <= n ; ++i)
for(int j = 1 ; j <= 7 ; ++j)
a[i][j] = (int)sqrt(a[i][j - 1]);
t = sqrt(n);
int len = t;
R[0] = 0;
for(int i = 1 ; i <= t ; ++i)
L[i] = R[i - 1] + 1 , R[i] = i * len;
if(R[t] < n)
++t , L[t] = R[t - 1] + 1 , R[t] = n;
cnt = t * 7;
for(int i = 1 ; i <= t ; ++i)
for(int j = L[i] ; j <= R[i] ; ++j)
pos[j] = i;
for(int i = 1 ; i <= t ; ++i)
for(int j = 0 ; j <= 7 ; ++j)
for(int k = L[i] ; k <= R[i] ; ++k)
sum[i][j] += a[k][j];
}
void change_(int l , int r , int p) {
if(upd[p] == 7)return;//注意这里的优化,挺重要的,学校的台式机(lao ren ji)测极限数据可从2000ms优化到200ms
for(int i = l ; i <= r ; ++i)
for(int j = 0 ; j <= 6 ; ++j)
a[i][j] = a[i][j + 1];
for(int j = 0 ; j <= 7 ; ++j) {
sum[p][j] = 0;
for(int i = L[p] ; i <= R[p] ; ++i)
sum[p][j] += a[i][j];
}
}
void change(int l , int r) {
if(cnt <= 0)return;
int p = pos[l] , q = pos[r];
if(p == q) {
change_(l , r , p);
return;
}
change_(l , R[p] , p);
for(int i = p + 1 ; i <= q - 1 ; ++i)
if(upd[i] < 7)++upd[i],--cnt;
change_(L[q] , r , q);
}
ll query(rr int l , rr int r) {
rr int p = pos[l] , q = pos[r];
rr ll ans = 0;
if(p == q) {
for(int i = l ; i <= r ; ++i)
ans += a[i][upd[p]];
return ans;
}
for(rr int i = l ; i <= R[p] ; ++i)
ans += a[i][upd[p]];
for(rr int i = p + 1 ; i <= q - 1 ; ++i)
ans += sum[i][upd[i]];
for(rr int i = L[q] ; i <= r ; ++i)
ans += a[i][upd[q]];
return ans;
}
void output(ll x) {
if(x >= 10)output(x / 10);
putchar(x % 10 + '0');
}
int main() {
n = read();
for(rr int i = 1 ; i <= n ; i++)
a[i][0] = read();
Init();
m = read();
for(int i = 1 ; i <= m ; ++i) {
int op = read() , l = read() , r = read();
if(l > r){rr int tmp = l ; l = r ; r = tmp;}
if(op == 0) change(l , r);
else {
/*
printf("%lld\n" , query(l , r));
/*/
output(query(l , r));
putchar('\n');
//*/
}
}
return 0;
}
暴力
#include <bits/stdc++.h>
#define ll long long
using namespace std;
long long read() {
long long re = 0;
char c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0',
c = getchar();
return re;
}
int n , m;
ll a[100010];
int main() {
n = read();
for(int i = 1 ; i <= n ; i++)
a[i] = read();
m = read();
for(int i = 1 ; i <= m ; i++) {
int op = read() , l = read() , r = read();
if(l > r) swap(l , r);
if(op == 0)
for(int j = l ; j <= r ; j++)
a[j] = (int)sqrt(a[j]);
else {
ll ans = 0;
for(int j = l ; j <= r ; j++) {
ans += a[j];
}
printf("%lld\n" , ans);
}
}
return 0;
}
数据生成
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll random(ll r , ll l = 1) {
return l == r ? l : abs((ll)rand() * rand() % (r - l) + l);
}
int main() {
srand((unsigned)(time(0)));
int n = random(100000 , 100000);
printf("%d\n" , n);
for(int i = 1 ; i <= n ; i++)
printf("%lld " , random(1000000000000ll));
putchar('\n');
int m = random(100000 , 100000);
printf("%d\n" , m);
for(int i = 1 ; i <= m ; i++) {
printf("%d %d %d\n" , random(100) % 2 ^ 1 , random(n) , random(n));
}
return 0;
}
对拍控制
#include <bits/stdc++.h>
using namespace std;
int main() {
int cnt = 0;
while(true) {
++cnt;
printf("\n#%d\n" , cnt);
system("random.exe > input.txt");
puts("random");
int t = clock();
if(system("tested < input.txt > output2.txt") != 0) {
cout << "RE";
break;
}
puts("tested");
printf(">time : %d\n" , clock() - t);
continue;//删掉这里开启对拍,否则为单纯性能测试
system("std.exe < input.txt > output1.txt");
puts("std");
if(system("fc output1.txt output2.txt")) {
cout << "WA";
break;
}
}
system("start input.txt");
return 0;
}