CodeChef - DGCD——樹鏈剖分+差分

【題目描述】

 You're given a tree on N vertices. Each vertex has a positive integer written on it, number on the ith vertex being vi. Your program must process two types of queries :1. Find query represented by F u v : Find out gcd of all numbers on the unique path between vertices u and v in the tree (both inclusive).2. Change query represented by C u v d : Add d to the number written on all vertices along the unique path between vertices u and v in the tree (both inclusive).

Input

First line of input contains an integer N denoting the size of the vertex set of the tree. Then follow N - 1 lines, ith of which contains two integers ai and bi denoting an edge between vertices ai and bi in the tree. After this follow N space separated integers in a single line denoting initial values vi at each of these nodes. Then follows a single integer Q on a line by itself, denoting the number of queries to follow. Then follow Q queries, each one on a line by itself. Each query is either a find query or a change query with format as given in problem statement. Note that all vertices are 0-based.

Output

For every find query, print the answer to that query in one line by itself.

Example

Input:

6
0 4
0 5
1 5
5 2
3 5
3 1 3 2 4 2
5
F 3 5
C 1 3 1
C 3 4 4
F 3 0
F 2 5

Output:

2
7
1

Constraints

1 <= N <= 50000
1 <= Q <= 50000
0 <= u, v <= N-1
1 <= vi <= 10^4
0 <= d <= 10^4 

【題目分析】
歷經一天我終于搞出這道題了。其實用半天寫了代碼,但是另外半天都在調,因為用位操作符的時候少寫了一個小于好導致數組越界發生玄學錯誤,我愣是研究了一下午加一晚上都沒有發現,各種調試覺得是電腦出問題了,我明明沒有修改那個數組的值但是他的值莫名其妙就改了。。。
今天早上發現這個錯誤后直接A了。
自己看的時候還是不會,覺得就算用樹鏈剖分用線段樹維護區間GCD可是人家還有區間修改怎么辦,總不能區間修改以后暴力維護一遍區間GCD吧。學習了一下其他大佬的博客后發現一個比較有(xuan)趣(xue)的結論:gcd(x1,x2,x3...)=gcd(x1,x2?x1,x3?x2,...)gcd(x1,x2,x3...)=gcd(x1,x2-x1,x3-x2,...)gcd(x1,x2,x3...)=gcd(x1,x2?x1,x3?x2,...)
然后我們就發現區間GCD和差分結合在一起啦啦啦啦。對于每一個區間,我們進行一次單點查詢x1,再進行一次區間查詢后面的維護的GCD。即gcd(x1,x2,x3...)=gcd(val1[x1],val2[x2..xn])gcd(x1,x2,x3...)=gcd(val1[x1],val2[x2..xn])gcd(x1,x2,x3...)=gcd(val1[x1],val2[x2..xn]),其中val2維護的是差分區間的GCD值,val1維護的是原來的數值。
這樣做的意義就是讓區間修改變成單點修改:對于每次區間修改,我們發現對于差分區間內部來講其實只改變了第一個的值,其他的都沒有改變(大家都增加了,減掉后值就沒有改變)。
為了每次方便查詢,我們用兩個樹鏈分別維護原來的值和差分的值,查詢的時候分別對兩個進行單點查詢和區間查詢。
每次修改我們對原來的值的樹鏈進行區間修改,對維護差分GCD的樹鏈進行單點修改:這個和我們進行儲存的方式息息相關,在這里我們是從重鏈往下,將兒子節點的值進行差分(父親節點-兒子節點)(理解這個十分重要)。因此如果我們修改一個區間,重鏈的頭節點的值會增加val(因為沒有什么減他),修改的區間在這條鏈的最后的節點的兒子節點(如果有的話)會增加val(因為最后的節點的值增加了,兒子節點的值為最后節點減去兒子節點),如果區間沒有到達重鏈的頭節點,那么區間最前面的點的值會減小val(因為他的值應該是父親節點減去他,他的值增加而父親節點的值沒有變化所以他的值要減小val)。可能一開始還是有些難以理解(我理解了半天),可以對照著代碼。代碼應該還是比較清晰的。(注意區間修改是val1區間,單點修改是val2區間,區間查詢是val2區間,單點查詢是val1區間)
【AC代碼】

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=5e4+5;
int head[MAXN],fa[MAXN],pos[MAXN],A[MAXN];
int a[MAXN],sz[MAXN],son[MAXN],deep[MAXN],top[MAXN];
struct node
{int v,next;
}Edge[MAXN<<1];
int tot=0,cnt=0;
int n,m;
int val1[MAXN<<2],add[MAXN<<2],val2[MAXN<<2];void AddEdge(int u,int v)
{tot++;Edge[tot].v=v; Edge[tot].next=head[u];head[u]=tot;
}void dfs1(int u,int f)
{fa[u]=f; deep[u]=deep[f]+1;son[u]=0; sz[u]=1;for(int i=head[u];i;i=Edge[i].next){int v=Edge[i].v;if(v!=f){dfs1(v,u);sz[u]+=sz[v];if(sz[son[u]]<sz[v]) son[u]=v;}}
}void dfs2(int u,int f,int k)
{top[u]=k; pos[u]=++cnt; A[cnt]=a[u];if(!son[u]) return;if(son[u]) dfs2(son[u],u,k);for(int i=head[u];i;i=Edge[i].next){int v=Edge[i].v;if(v!=f && v!=son[u]) dfs2(v,u,v);}
}void test()
{for(int i=1;i<13;i++){printf("%d ",add[i]);}printf("%d\n",add[13]);
}void Build(int k,int l,int r)
{add[k]=0;if(l==r){val1[k]=A[l];return;}int mid=(l+r)>>1;Build(k<<1,l,mid);Build(k<<1|1,mid+1,r);
}int gcd(int a,int b)
{return b?gcd(b,a%b):abs(a);
}void change_point(int k,int l,int r,int x,int val)
{if(l==r){val2[k]+=val;return;}int mid=(l+r)>>1;if(x<=mid) change_point(k<<1,l,mid,x,val);else change_point(k<<1|1,mid+1,r,x,val);val2[k]=gcd(val2[k<<1],val2[k<<1|1]);
}void pushdown(int k)
{if(add[k]){add[k<<1]+=add[k]; add[k<<1|1]+=add[k];val1[k<<1]+=add[k]; val1[k<<1|1]+=add[k];add[k]=0;}
}void change_interval(int k,int l,int r,int L,int R,int val)
{if(l>=L && r<=R){add[k]+=val;val1[k]+=val;return;}int mid=(l+r)>>1;pushdown(k);if(L<=mid) change_interval(k<<1,l,mid,L,R,val);if(R>mid) change_interval(k<<1|1,mid+1,r,L,R,val);
}void change_tree(int u,int v,int w)
{while(top[u]!=top[v]){if(deep[top[u]]<deep[top[v]]) swap(u,v);change_interval(1,1,n,pos[top[u]],pos[u],w);if(son[u]) change_point(1,1,n,pos[son[u]],w);u=fa[top[u]];}if(deep[u]<deep[v]) swap(u,v);change_interval(1,1,n,pos[v],pos[u],w);if(son[u]) change_point(1,1,n,pos[son[u]],w);if(son[fa[v]]==v) change_point(1,1,n,pos[v],-w);
}int query_interval(int k,int l,int r,int L,int R)
{if(l>=L && r<=R){return val2[k];}int mid=(l+r)>>1;if(R<=mid) return query_interval(k<<1,l,mid,L,R);else if(L>mid) return query_interval(k<<1|1,mid+1,r,L,R);else return gcd(query_interval(k<<1,l,mid,L,mid),query_interval(k<<1|1,mid+1,r,mid+1,R));
}int query_point(int k,int l,int r,int x)
{if(l==r && l==x){return val1[k];}int mid=(l+r)>>1;pushdown(k);if(x<=mid) return query_point(k<<1,l,mid,x);else return query_point(k<<1|1,mid+1,r,x);
}int query_tree(int u,int v)
{int ret=0;while(top[u]!=top[v]){if(deep[top[u]]<deep[top[v]]) swap(u,v);if(top[u]!=u) ret=gcd(ret,query_interval(1,1,n,pos[son[top[u]]],pos[u]));ret=gcd(ret,query_point(1,1,n,pos[top[u]]));u=fa[top[u]];}if(deep[u]<deep[v]) swap(u,v);if(u!=v) ret=gcd(ret,query_interval(1,1,n,pos[son[v]],pos[u]));ret=gcd(ret,query_point(1,1,n,pos[v]));return ret;
}void Read()
{int u,v;scanf("%d",&n);for(int i=1;i<n;i++){scanf("%d%d",&u,&v);AddEdge(u+1,v+1); AddEdge(v+1,u+1);}for(int i=1;i<=n;i++) scanf("%d",&a[i]);// test();
}void Init()
{dfs1(1,0); dfs2(1,0,1);Build(1,1,n);for(int i=1;i<=n;i++){if(son[i])change_point(1,1,n,pos[son[i]],a[i]-a[son[i]]);}
}void Solve()
{char cmd[10];int u,v,w;scanf("%d",&m);while(m--){scanf("%s",cmd);if(cmd[0]=='F'){scanf("%d%d",&u,&v);printf("%d\n",query_tree(u+1,v+1));}else{scanf("%d%d%d",&u,&v,&w);change_tree(u+1,v+1,w);}}
}int main()
{Read();Init();Solve();return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384041.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384041.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384041.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

UVa272-TeX中的引號

【題目描述】 傳送門 【題目分析】 今天開始刷紫書的題目啦 這道題很簡單&#xff0c;需要注意的是cgetchar()需要加上括號&#xff0c;因為賦值語句的優先級比判等低 而且書中說好像最好用整型變量&#xff0c;因為EOF的值為-1&#xff0c;在字符變量中沒有這個值。&#xf…

C++中友元(友元函數和友元類)的用法和功能

http://blog.csdn.net/adriano119/article/details/5914443/ 采用類的機制后實現了數據的隱藏與封裝&#xff0c;類的數據成員一般定義為私有成員&#xff0c;成員函數一般定義為公有的&#xff0c;依此提供類與外界間的通信接口。但是&#xff0c;有時需要定義一些函數&#x…

線程的終止分離

1.線程的終止 注意該函數是針對用戶級別的, 其中 retal 必須指向一個全局變量, 或者是一個 malloc 分配的, 因為如果是線程的局部變量, 當該線程退出時, 其他線程不能得到這個變量, 因為線程的局部變量各自私有 2. 現成的取消 其中thread是線程的 tid 3.線程的等待與分離 (1)…

UVa10082

【題目描述】 傳送門 【題目分析】 同樣是一道模擬&#xff0c;但是如何巧妙快速的解決仍然不簡單。通過這道題告訴我們對于復雜確定的對應關系我們要靈活運用常量數組。 同時還需要注意的一個小問題就是字符串數組中的"//"指的是轉義后的單斜杠&#xff0c;如果只…

C語言中的深拷貝和淺拷貝

http://www.cnblogs.com/zhanggaofeng/p/5421804.html C語言中的深拷貝和淺拷貝 //C語言中的深拷貝和淺拷貝 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string.h>typedef struct _student{char name[30];char *title;…

死鎖的產生和避免

1.死鎖產生的四個必要條件 (1)互斥條件&#xff1a;資源是獨占的且排他使用&#xff0c;進程互斥使用資源&#xff0c;即任意時刻一個資源只能給一個進程使用&#xff0c;其他進程若申請一個資源&#xff0c;而該資源被另一進程占有時&#xff0c;則申請者等待直到資源被占有者…

UVa401

【題目描述】 傳送門 【題目描述】 嘻嘻&#xff0c;自己做直接AC還是比較開心的。當然有一部分原因是之前看書的時候詳細看過這個題的代碼&#xff0c;但是這已經快一年了&#xff0c;應該說做出這道題憑借的是自己的能力吧。回過身去看了一下書中的代碼發現自己寫的不算復…

gethostbyname() 函數說明

https://www.cnblogs.com/cxz2009/archive/2010/11/19/1881611.html gethostbyname()函數說明——用域名或主機名獲取IP地址 包含頭文件#include <netdb.h>#include <sys/socket.h>函數原型struct hostent *gethostbyname(const char *name);這個函數的傳入值是域…

求解迷宮最短路徑

1. 多通路迷宮初始化 先構建一個多通路迷宮,并且對其初始化 void MazeInitShortPath(Maze* maze) {if(maze NULL){return;}int row 0;int col 0;for(; row < MAX_COL; row){for(col 0; col < MAX_COL; col){maze -> map[row][col] Map[row][col];}printf("…

UVa340

【題目描述】 傳送門 【題目分析】 題目理解以后十分簡單&#xff0c;但是這題面實在讓人自閉&#xff0c;這么簡單的題目啦啦啦啦說了那么多&#xff0c;實在是看不懂。&#xff08;幸虧我看了書理解了題目的意思&#xff0c;要不然。。&#xff09;還是要鍛煉自己的讀題能…

C語言:結構體中一級指針和二級指針的創建與釋放示例

http://blog.csdn.net/Bixiwen_liu/article/details/53610952 這幾天把C語言鞏固了一下&#xff0c;作為一門最基本的編程語言&#xff0c;C語言還是相當基礎和非常重要的&#xff0c;個人認為C語言還是很有必要學好吃透的。 今天寫的話題是結構體結構體中一級指針和二級指針的…

帶環迷宮求最短路徑

前面介紹了簡單的迷宮求解問題, 今天我們就對帶環迷宮求出它的最短路徑 1.首先來看一個帶環迷宮的簡單地圖 在這張迷宮地圖中,我們規定入口點的位置entry的坐標是 (0, 1), 同時, 我們給入口點傳一個非法坐標,作為入口點的前一個位置(-1, -1). 接下來的思路就和上一篇的思路是一…

UVa1583

【題目描述】 傳送門 【題目分析】 我以為很簡單就寫了一個暴力沒有想到超時了。應該是T是非常大的所以必須得打表&#xff0c;將所有的結果都儲存起來然后直接輸出。 以后遇到這種可以一下算出所有結果的多組數據最好還是算出所有的結果然后再輸出答案。 【AC代碼】 #inc…

C 結構體嵌套一級指針 二級指針 動態分配內存

https://blog.csdn.net/xielinhua88/article/details/51364623 點擊打開鏈接 #define _CRT_SECURE_NO_WARNINGS #include <stdlib.h> #include <string.h> #include <stdio.h> //結構體嵌套一級指針 二級指針 動態分配內存 typedef struct _Teacher { int ag…

線程的同步與互斥

1. 互斥量 在Linux 下線程使用的都是局部變量, 而我們知道, 每一個線程都獨立擁有自己的一個棧, 而這些局部便令就在棧中,而線程的創建就是為了實現通信, 此時線程之間無法共享這些變量 ????為了使得線程之間能夠共享數據, 一次我們可以創建一個全局變量, 此時線程都在進程…

二級指針與指針數組的關系

http://blog.csdn.net/shuaishuai80/article/details/6129742 #include <stdio.h> void test(char *argv[]); int main(void) { char *argv[3]{{"abcdefg"},{"1234567"},{"q1w2e3r"}}; test(argv); /*調用指針數組…

UVa1584

【題目描述】 傳送門 【題目分析】 也是一道簡單的模擬題&#xff0c;1A嘿嘿嘿。 再看書發現和書上的做法差不多。 【AC代碼】 #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<cstd…

cf#582div3 D——暴力

【題目描述】 The only difference between easy and hard versions is the number of elements in the array.You are given an array a consisting of n integers. In one move you can choose any ai and divide it by 2 rounding down (in other words, in one move you c…

C語言 可變參數

http://www.cnblogs.com/zhanggaofeng/p/6434554.html //可變參數 #include <stdio.h> #include <stdlib.h> #include <string.h> //引用頭文件 #include <stdarg.h>/* va_list用于聲明一個變量&#xff0c;我們知道函數的可變參數列表其實就是一個字符…

UVa1585

【題目描述】 傳送門 【題目分析】 氵 【AC代碼】 #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<cstdlib> #include<set> #include<map> #include<vector>u…