日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CodeChef - DGCD——树链剖分+差分

發(fā)布時間:2023/11/30 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CodeChef - DGCD——树链剖分+差分 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

【題目描述】

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

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

總結

以上是生活随笔為你收集整理的CodeChef - DGCD——树链剖分+差分的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。