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,x2x1,x3x2,...)
然后我们就发现区间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.mzph.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…