1. 程式人生 > >【樹鏈剖分】Codechef DGCD

【樹鏈剖分】Codechef DGCD

題意:

給出一棵點權樹,有M次操作:
1、詢問一條路徑上的GCD
2、將一段路徑上的點權加上d


分析:

如果這題在一個序列上就非常美妙了:
利用輾轉相減法: G C D ( a , b

) = G C D ( a b , b
) GCD(a,b)=GCD(a-b,b)

所以如果這個問題在一個序列上:
設為 A 1 , A

2 , A n A_1,A_2,……A_n
就可以利用差分,變為:
A 1 , A 2 A 1 , A 3 A 2 , A 4 A 3 A n A n 1 A_1,A_2-A_1,A_3-A_2,A_4-A-3……A_n-A_{n-1}
此時的區間修改就變為兩次單點修改了。

現在把這個問題轉移到樹上。。。。就很板了。。。
直接樹鏈剖分,然後對每個鏈,當成一個序列來做。

然後修改的時候注意一下就行了(笑)。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define SF scanf
#define PF printf
#define MAXN 50010
using namespace std;
int dep[MAXN],son[MAXN],fa[MAXN],sz[MAXN],tid[MAXN],rnk[MAXN],top[MAXN],v[MAXN];
int tree[MAXN*4],tag[MAXN*4];
int n,dcnt;
vector<int> a[MAXN];
int gcd(int x,int y){
	if(y==0)
		return x;
	return gcd(y,x%y);	
}
void dfs1(int x,int fax){
	dep[x]=dep[fax]+1;
	fa[x]=fax;
	son[x]=-1;
	sz[x]=1;
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fax)
			continue;
		dfs1(u,x);	
		sz[x]+=sz[u];
		if(son[x]==-1||sz[son[x]]<sz[u])
			son[x]=u;
	}
}
void dfs2(int x,int tp){
	top[x]=tp;
	tid[x]=++dcnt;
	rnk[dcnt]=x;
	if(son[x]==-1)
		return ;
	dfs2(son[x],tp);
	for(int i=0;i<a[x].size();i++){
		int u=a[x][i];
		if(u==fa[x]||u==son[x])
			continue;
		dfs2(u,u);	
	}
}
void build(int l=1,int r=n,int id=1){
	if(l==r){
		if(top[rnk[l]]!=rnk[l])
			tree[id]=v[fa[rnk[l]]]-v[rnk[l]];	
		else
			tree[id]=v[rnk[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,id<<1);
	build(mid+1,r,id<<1|1);
	tree[id]=gcd(tree[id<<1],tree[id<<1|1]);
}
void change_on_point(int pos,int val,int l=1,int r=n,int id=1){
	if(l==r){
		tree[id]+=val;
		return ;	
	}
	int mid=(l+r)>>1;
	if(pos<=mid)
		change_on_point(pos,val,l,mid,id<<1);
	else
		change_on_point(pos,val,mid+1,r,id<<1|1);
	tree[id]=gcd(tree[id<<1],tree[id<<1|1]);
}
void change_on_road(int l1,int r1,int val,int l=1,int r=n,int id=1){
	if(l>=l1&&r<=r1){
		tag[id]+=val;
		return ;	
	}
	int mid=(l+r)>>1;
	if(l1<=mid)
		change_on_road(l1,r1,val,l,mid,id<<1);
	if(r1>mid)
		change_on_road(l1,r1,val,mid+1,r,id<<1|1);
}
int query_on_point(int pos,int l=1,int r=n,int id=1){
	if(l==r)
		return v[rnk[l]]+tag[id];
	int mid=(l+r)>>1;
	if(pos<=mid)
		return query_on_point(pos,l,mid,id<<1)+tag[id];
	else
		return query_on_point(pos,mid+1,r,id<<1|1)+tag[id];	
}
int query_on_road(int l1,int r1,int l=1,int r=n,int id=1){
	if(l>=l1&&r<=r1)
		return tree[id];
	int mid=(l+r)>>1;
	int res1=0,res2=0;
	if(l1<=mid)
		res1=query_on_road(l1,r1,l,mid,id<<1);
	if(r1>mid)
		res2=query_on_road(l1,r1,mid+1,r,id<<1|1);
	return gcd(res1,res2);
}
void change_on_tree(int u,int v,int val){
	if(u==v){
		if(top[u]!=u)
			change_on_point(tid[u],-val);
		else
			change_on_point(tid[u],val);
		if(son[u]!=-1)
			change_on_point(tid[son[u]],val);
		change_on_road(tid[u],tid[u],val);
		return ;
	}
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		change_on_road(tid[top[u]],tid[u],val);
		change_on_point(tid[top[u]],val);
		if(son[u]!=-1)
			change_on_point(tid[son[u]],val);
		u=fa[top[u]];
	}
	if(dep[u]>dep[v])
		swap(u,v);
	change_on_road(tid[u],tid[v],val);
	if(top[u]!=u)
		change_on_point(tid[u],-val);
	else
		change_on_point(tid[u],val);
	if(son[v]!=-1)
		change_on_point(tid[son[v]],val);
}
int query_on_tree(int u,int v){
	if(u==v)
		return query_on_point(tid[u]);
	int res=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		res=gcd(res,query_on_road(tid[top[u]],tid[u]));
		u=fa[top[u]];
	}
	if(dep[u]>dep[v])
		swap(u,v);
	if(u!=v)
		res=gcd(res,query_on_road(tid[u]+1,tid[v]));
	res=gcd(res,query_on_point(tid[u]));
	return res;
}
char s[20];
int main(){
	SF("%d",&n);
	int x,y,val;
	for(int i=1;i<n;i++){
		SF("%d%d",&x,&y);
		x++;
		y++;
		a[x].push_back(y);
		a[y].push_back(x);	
	}
	for(int i=1;i<=n;i++)
		SF("%d",&v[i]);
	dfs1(1,0);
	dfs2(1,1);
	build();
	int m;
	SF("%d",&m);
	for(int i=1;i<=m;i++){
		SF("%s",s);
		if(s[0]=='F'){
			SF("%d%d",&x,&y);
			x++,y++;
			PF("%d\n",abs(query_on_tree(x,y)));
		}
		else{
			SF("%d%d%d",&x,&y,&val);
			x++,y++;
			change_on_tree(x,y,val);
		}
	}
}

順便提供資料生成器:

#include<cstdio>
#include<cstdlib>
#include<ctime>
#define SF scanf
#define PF printf
#define MAXN 20
#define MAXM 20
#define MAXV 20
using namespace std;
int get_rand(int x)
            
           

相關推薦

Codechef DGCD

題意: 給出一棵點權樹,有M次操作: 1、詢問一條路徑上的GCD 2、將一段路徑上的點權加上d 分析: 如果這題在一個序列上就非常美妙了: 利用輾轉相減法: G

洛谷P3379 求LCA

pri else ios class 論文 main 我們 嚴格 所有 題目描述 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 輸入輸出格式 輸入格式: 第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的序號。 接下來N-1行

4034. [HAOI2015]樹上操作

uil hint scanf -m oid 輸入數據 content 其中 HA Description 有一棵點數為 N 的樹,以點 1 為根,且樹點有邊權。然後有 M 個 操作,分為三種: 操作 1 :把某個節點 x 的點權增加 a 。 操作 2 :把某個節

3999. [TJOI2015]旅遊

== urn ati hang script 線段樹 城市編號 turn esc Description 為了提高智商,ZJY準備去往一個新世界去旅遊。這個世界的城市布局像一棵樹。每兩座城市之間只有一條路徑可 以互達。每座城市都有一種寶石,有一定的價格。ZJY為了賺

LOJ2269 [SDOI2017] 切遊戲 FWT動態DP線段

stack dep 根據 註意 樹形dp 沒有 top cto HERE 題目分析: 好題。本來是一道好的非套路題,但是不湊巧的是當年有一位國家集訓隊員正好介紹了這個算法。 首先考慮靜態的情況。這個的DP方程非常容易寫出來。 接著可以註意到對於異或結果的計數可以看成一個F

bzoj [Usaco2010 Hol]cowpol 奶牛政壇

clu int == fat void += char tdi ios 意識流虛樹 首先考慮只有一個黨派,那麽可以O(n)求樹的直徑,步驟是隨便指定一個根然後找距離根最遠點,然後再找距離這個最遠點最遠的點,那麽最遠點和距離這個最遠點最遠的點之間的距離就是直徑 那麽考慮多黨派

UOJ268 [清華集訓2016] 數據交互 動態DP線段

auto 合並 -s 樹形dp lazy tail clas 記錄 less 題目分析: 不難發現可以用動態DP做。 題目相當於是要我求一條路徑,所有與路徑有交的鏈的代價加入進去,要求代價最大。 我們把鏈的代價分成兩個部分:一部分將代價加入$LCA$之中,用$g$數組保存;

洛谷P3038 [USACO11DEC]牧草種植邊權修改與查詢

【連結】 https://www.luogu.org/problemnew/show/P3038 【題意】 給出一棵n個節點的樹,有m個操作,操作為將一條路徑上的邊權加一或詢問某條邊的權值。 【思路】 樹鏈剖分的裸題。 但是這個題是在邊上進行操作,我們考慮把邊上的操作轉移到點

BZOJ1036的統計

Description   一棵樹上有n個節點,編號分別為1到n,每個節點都有一個權值w。我們將以下面的形式來要求你對這棵樹完成 一些操作: I. CHANGE u t : 把結點u的權值改為t II. QMAX u v: 詢問從點u到點v的路徑上的節點的最大權值 I II. QSUM u v:

[JZOJ5909]NOIP2018模擬10.16跑商圓方

Description 基三的地圖可以看做 n 個城市,m 條邊的無向圖,尊者神高達會從某個點出發並在起點購買貨物,在旅途中任意一點賣出並最終到達終點,尊者神高達的時間很寶貴,所以他不會重複經過同一個城市,但是為了掙錢,他可能會去繞路。當然,由於工作室氾濫,所以一個城市的貨物

Jiu Yuan Wants to Eat2018焦作網路賽

題目連結 樹鏈剖分學習筆記,可以看這裡   這道題還真挺好的,以前不會做,現在想了發現,學過樹鏈剖分之後,剩下的部分就是處理去反那塊比較的不容易些了,但是想了一下午,現在還是給我敲出來了,我們主要難處理的就是關於求反,那麼怎麼處理求反?   一開始讀題的時候,我還

Aragorn's Story HDU - 3966

題目連結 樹鏈剖分的學習筆記(更新中)   這道題所給的Hint好有迷惑性,它跟我們說注意士兵的數量可能為負數,我的第一反應是,士兵的數量是不是不能為負數,那麼我們是不是要做出些什麼調整,然而,語文不好的我看了Discuss才知道說的是:士兵的數量可以為負數。這樣也好,題目就變

樹上的詢問

                                            【題目描述】 給你一棵具有N個點(編號為1到N)M條邊的樹,並給定各個點權的值,然後有3種操作: I C1 C2 K:把C1與C2的路徑上的所有點權值加上K D C1 C2 K:把C1與

洛谷P3379lca

【連結】 【題意】 求lca 【程式碼】 #include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 6; vector<int>v[maxn]; int a[ma

2018 Multi-University Training Contest 7 1008 Traffic Network in Numazu

題意:NN個節點NN條邊的連通圖,有刪改操作和線上查詢兩點間的最短路。 分析:相當於是一顆樹上多了一條邊,那麼找到一條這樣的邊(滿足刪除之後餘下整體為樹)把它刪掉。對於兩點間的查詢,由於有修改,就採用樹鏈剖分跑線段樹的方法來解決就OK。最後在計算答案的時候

校內賽 codeforces 827D最小生成樹 解題報告

找不到題面!! 題意 給出一張n(<=2e5)個點 m(<=2e5)條邊無向圖,保證有生成樹。對於每條邊,給出一個最大值maxLength,咦即能夠保證這條邊能夠出現在所有的最小生成樹中,邊權的最大值為maxLength(同時,其他所有邊長度不變

Codeforces網路流線段ALT (CodeForces - 786E)

題意 現在有m個人,每一個人都特別喜歡狗。另外還有一棵n個節點的樹。 現在每個人都想要從樹上的某個節點走到另外一個節點,且滿足要麼這個人自帶一條狗m,要麼他經過的所有邊h上都有一條狗。 2<=n<=2*10^4,1<=m<=10^4 輸入格式 第一行為兩個整數n,m,分

BZOJ2434 [NOI2011] 阿狸的打字機 線段failAC自動機

題目分析: 畫一下fail樹,就會發現就是x的子樹中屬於y路徑的,把y剖分一下,用線段樹處理 $O(n*log^2 n)$。 程式碼: 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int ma

bzoj1036的統計CountZKW大法好卡常大法好

關於這個樹上路徑端點會重合的問題,我們只要不判斷x==y就行了。詳見被註釋呵呵的地方。 #include<cstdio> #include<cstring> #include<iostream> using namesp

ACM-ICPC 2018 焦作賽區網路預賽 E Jiu Yuan Wants to Eat

Jiu Yuan Wants to Eat Time Limit: 3000 MS Memory Limit: 65536 K Problem Description You ye Jiu yuan is the daughter of the Great G