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;
}
posted @ 2020-11-26 20:27  追梦人1024  阅读(60)  评论(0编辑  收藏  举报