1. 程式人生 > 實用技巧 >P4145 上帝造題的七分鐘2 / 花神遊歷各國 題解

P4145 上帝造題的七分鐘2 / 花神遊歷各國 題解

題解 上帝造題的七分鐘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,最後加了一點玄學才過,果然是人傻常數大。