1. 程式人生 > 實用技巧 >SP2713 GSS4 題解

SP2713 GSS4 題解

SP2713 GSS4 題解

間隙

雙倍經驗

前置知識:線段樹

如果您還不會線段樹的話,推薦去看一下這篇文章,我一開始也是在那裡學的

大致題意

給一堆數,有以下兩個操作:

  • 給出一個區間\([L,R]\),把該區間內的每個數都開平方

  • 給出一個區間\([L,R]\),查詢這個區間的每個數的和

分析

首先看一下這個資料範圍,\(1e18\),直接暴力的話肯定會T飛

求和操作很簡單,相信學過線段樹的人應該都會

難點在於這個開方操作,我們沒法像線段樹模板那樣打個懶標記來進行下傳操作

通過觀察\(\sqrt x\)函式影象緩慢的增長率或者其他性質不難發現,很多開方操作是不必要的,考慮減枝優化:

  • 不難發現,當一個區間內的所有數都是\(1\)時,再對該區間進行開方操作對該區間內的總值造成不了任何改變(\(\sqrt{1} = 1\))

因此程式碼實現方面只要在區間內總值均為1的情況下加一個小剪枝即可


程式碼實現

思路理解了程式碼實現難度就不高了,但還是有幾個坑點...具體的註釋裡有寫

#include<bits/stdc++.h>
#define lson (node<<1)//左兒子
#define rson (node<<1|1)//右兒子
#define ll long long 
#define int long long //記得開long long
using namespace std;
const int MAXN = 100005;
struct st{
	int l,r;//左右端點
	ll sum;
}tree[MAXN<<2];

ll a[MAXN];
int n,m;
void pushup(int node){
	tree[node].sum = tree[lson].sum + tree[rson].sum;//合併操作
}
void build(int node,int l,int r){//建樹
	tree[node].l = l;
	tree[node].r = r;
	if(l==r){
		tree[node].sum = a[l];
		return;
	}
	int mid = (l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(node);
}
void change(int node,int l,int r){
	int L = tree[node].l,R = tree[node].r;
	if(tree[node].sum==R-L+1) return;//如果總和為區間長度,也就是所有值均為1時,直接剪枝掉
	if(L==R){
		tree[node].sum = sqrt(tree[node].sum);
		return;
	}
	int mid = (L+R)>>1;
	if(l<=mid){
			change(lson,l,r);
	}
	if(r>mid){
			change(rson,l,r);
	}
	pushup(node);
}
ll query(int node,int l,int r){//查詢
	int L = tree[node].l,R = tree[node].r;
	if(l<=L&&r>=R){//包含在查詢區間內,直接返回sum值
		return tree[node].sum;
	}
	int mid = (L+R)>>1;
	ll ans = 0;
	if(l<=mid){
		ans+=query(lson,l,r);
	}
	if(r>mid){
		ans+=query(rson,l,r);
	}
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);//不加貌似會TLE?
    cin.tie(0);
    cout.tie(0);
	int Case=0;
	while(cin>>n){
	printf("Case #%d:\n",++Case);//注意,樣例裡那個case是要輸出的,一開始被這裡卡了好久...
	memset(a,0,sizeof(a));//記得要先memset
	memset(tree,0,sizeof(tree));
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	cin>>m;
	while(m--){
		int mode,left,right;
		cin>>mode>>left>>right;
		if(left > right){
			swap(left,right);
		}
		if(mode==0){
			change(1,left,right);
		}
		else{
			printf("%lld\n",query(1,left,right));
		}
	 }
	 puts("");//記得換行
	}
	
}