题面

太长了,而且解释的不清楚,我来给个简化版的题意:

给定一棵$n$个点的数,每个点有点权,你需要实现以下$m$个操作

操作1,把$x$到$y$的路径上的所有点的权值都加上$delta$,并且更新一个版本

操作2,对于有向路径$(x,y)$上的点$a_i$,求下面的和值:

$\sum_{i=1}^{len} a_i \sum_{j=1}^{len-i} j$

操作3,回到第$i$个版本(但是下一次更新以后还是到总版本号+1的那个版本)

思路

这个题显然一眼就是树剖+可持久化数据结构啊

那么核心问题就是怎么用数据结构求上面那个和值

我们发现一个问题:上面那个东西里面,路径上最后一个点是没有贡献的

这个会让我们很难做

因此我们把这个式子加上一个东西再减掉,变成这个样子

$\sum_{i=1}^{len} a_i\ast S(len-i+1)-\sum_{i=1}^{len}(n-i+1)\ast a_i$

其中$S(i)$表示自然数求和

这样子就不用先把最后一个节点去掉了

然后我们把前面那个式子里面的$S$展开,发现得到这样的一个东西

$\sum_{i=1}^{len} a_i \frac{1}{2} (len^2-2leni + i^2 +3len-3i +2)$

我们把后面括号里的东西根据$i$的次数分类,得到下面的变换后式子

$\frac{1}{2}((n2+3n+2)\sum_{i=1}n a_i +(-2n-3)\sum_{i=1}^n ia_i + \sum_{i=1}^n i^2a_i)$

因为$n$是固定的,所以我们可以用线段树维护三个$\sum$里面的东西,并且用标记永久化来实现区间修改

然后其实这道题目就做完了

但是此题写起来极其恶心,因为你会发现你需要正着维护三个,还要反着维护3个

在询问的时候,$lca$两边的树链的正反是不一样的

标记永久化的时候标记上面的每一个节点收到的影响是不一样大的

等等恶心的问题

所以这实际上是一道毒瘤题,考场上遇到了千万别写

Code

比较复杂,我做了注释,可以参考一下

  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<cstdio>
  5. #include<cmath>
  6. #include<cassert>
  7. #define ll long long
  8. #define MOD 20160501
  9. #define inv2 10080251
  10. ll Sum2[100010];
  11. using namespace std;
  12. inline int read(){
  13. int re=0,flag=1;char ch=getchar();
  14. while(ch>'9'||ch<'0'){
  15. if(ch=='-') flag=-1;
  16. ch=getchar();
  17. }
  18. while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
  19. return re*flag;
  20. }
  21. ll qpow(ll a,ll b){
  22. ll re=1;
  23. while(b){
  24. if(b&1) re=re*a%MOD;
  25. a=a*a%MOD;b>>=1;
  26. }
  27. return re;
  28. }
  29. //树链剖分
  30. int n,first[100010],ori[100010],dep[100010],size[100010],son[100010],top[100010],pos[100010],back[100010],fa[100010];
  31. struct edge{
  32. int to,next;
  33. }e[200010];int cnte,clk;
  34. inline void addedge(int u,int v){
  35. e[++cnte]=(edge){v,first[u]};first[u]=cnte;
  36. e[++cnte]=(edge){u,first[v]};first[v]=cnte;
  37. }
  38. void dfs1(int u,int f){
  39. int i,v;
  40. dep[u]=dep[f]+1;
  41. fa[u]=f;
  42. size[u]=1;
  43. for(i=first[u];~i;i=e[i].next){
  44. v=e[i].to;if(v==f) continue;
  45. dfs1(v,u);
  46. size[u]+=size[v];
  47. if(size[son[u]]<size[v]) son[u]=v;
  48. }
  49. }
  50. void dfs2(int u,int t){
  51. int i,v;
  52. top[u]=t;
  53. clk++;
  54. pos[u]=clk;
  55. back[clk]=u;
  56. if(son[u]) dfs2(son[u],t);
  57. for(i=first[u];~i;i=e[i].next){
  58. v=e[i].to;if(v==son[u]||v==fa[u]) continue;
  59. dfs2(v,v);
  60. }
  61. }
  62. //segment tree with lazy tag stablized
  63. ll add[12000010];int ch[12000010][2],root[200010],now,cnt,tot;
  64. struct node{
  65. ll sum0,sum1,sum2,siz;
  66. node(ll s0=0,ll s1=0,ll s2=0,ll s=0){sum0=s0;sum1=s1;sum2=s2;siz=s;}
  67. bool empty(){return (sum1==0&&sum2==0&&sum0==0&&siz==0);}
  68. }a[12000010],rev[12000010];
  69. ll sqr(ll x){
  70. return x*x%MOD;
  71. }
  72. ll S1(ll x){自然数求和
  73. return x*(x+1)%MOD*inv2%MOD;
  74. }
  75. ll S2(ll x){//这里是自然数平方的前缀和
  76. return Sum2[x];
  77. }
  78. void merge(node &cur,node l,node r){//合并两个点
  79. if(l.empty()){cur=r;return;}
  80. if(r.empty()){cur=l;return;}
  81. cur.siz=l.siz+r.siz;
  82. cur.sum0=(l.sum0+r.sum0)%MOD;
  83. cur.sum1=(l.sum1+r.sum1+r.sum0*l.siz)%MOD;
  84. cur.sum2=(l.sum2+r.sum2+r.sum1*2*l.siz+r.sum0*sqr(l.siz))%MOD;
  85. }
  86. int build(int l,int r){
  87. int cur=++cnt;
  88. if(l==r){
  89. a[cur].siz=rev[cur].siz=1;
  90. a[cur].sum0=a[cur].sum1=a[cur].sum2=ori[back[l]];
  91. rev[cur].sum0=rev[cur].sum1=rev[cur].sum2=ori[back[l]];
  92. return cur;
  93. }
  94. int mid=(l+r)>>1;
  95. ch[cur][0]=build(l,mid);
  96. ch[cur][1]=build(mid+1,r);
  97. merge(a[cur],a[ch[cur][0]],a[ch[cur][1]]);
  98. merge(rev[cur],rev[ch[cur][1]],rev[ch[cur][0]]);
  99. return cur;
  100. }
  101. node get0(int l,int r,int ql,int qr,int cur,ll tmp){
  102. node re;
  103. if(l>=ql&&r<=qr){
  104. re=a[cur];
  105. (re.sum0+=re.siz*tmp)%=MOD;
  106. (re.sum1+=S1(re.siz)*tmp)%=MOD;
  107. (re.sum2+=S2(re.siz)*tmp)%=MOD;
  108. return re;
  109. }
  110. int mid=(l+r)>>1;
  111. if(mid>=qr) return get0(l,mid,ql,qr,ch[cur][0],tmp+add[cur]);
  112. else{
  113. if(mid<ql) return get0(mid+1,r,ql,qr,ch[cur][1],tmp+add[cur]);
  114. else{
  115. merge(re,get0(l,mid,ql,qr,ch[cur][0],tmp+add[cur]),get0(mid+1,r,ql,qr,ch[cur][1],tmp+add[cur]));
  116. }
  117. }
  118. return re;
  119. }
  120. node get1(int l,int r,int ql,int qr,int cur,ll tmp){
  121. node re;
  122. if(l>=ql&&r<=qr){
  123. re=rev[cur];
  124. (re.sum0+=re.siz*tmp)%=MOD;
  125. (re.sum1+=S1(re.siz)*tmp)%=MOD;
  126. (re.sum2+=S2(re.siz)*tmp)%=MOD;
  127. return re;
  128. }
  129. int mid=(l+r)>>1;
  130. if(mid>=qr) return get1(l,mid,ql,qr,ch[cur][0],tmp+add[cur]);
  131. else{
  132. if(mid<ql) return get1(mid+1,r,ql,qr,ch[cur][1],tmp+add[cur]);
  133. else{
  134. merge(re,get1(mid+1,r,ql,qr,ch[cur][1],tmp+add[cur]),get1(l,mid,ql,qr,ch[cur][0],tmp+add[cur]));
  135. }
  136. }
  137. return re;
  138. }
  139. int change(int l,int r,int ql,int qr,int pre,int val){
  140. int cur=++cnt;
  141. a[cur]=a[pre];rev[cur]=rev[pre];
  142. ch[cur][0]=ch[pre][0];ch[cur][1]=ch[pre][1];add[cur]=add[pre];
  143. //以下是更新值的部分
  144. (a[cur].sum0+=(qr-ql+1)*val)%=MOD;
  145. (rev[cur].sum0+=(qr-ql+1)*val)%=MOD;
  146. (a[cur].sum1+=val*(S1(qr-l+1)-S1(ql-l)+MOD))%=MOD;
  147. (rev[cur].sum1+=val*(S1(r-ql+1)-S1(r-qr)+MOD))%=MOD;
  148. (a[cur].sum2+=val*(S2(qr-l+1)-S2(ql-l)+MOD))%=MOD;
  149. (rev[cur].sum2+=val*(S2(r-ql+1)-S2(r-qr)+MOD))%=MOD;
  150. if(l==ql&&r==qr){
  151. (add[cur]+=val)%=MOD;
  152. return cur;
  153. }
  154. int mid=(l+r)>>1;
  155. if(mid>=ql) ch[cur][0]=change(l,mid,ql,min(mid,qr),ch[pre][0],val);
  156. if(mid<qr) ch[cur][1]=change(mid+1,r,max(mid+1,ql),qr,ch[pre][1],val);
  157. //标记永久化不需要update
  158. return cur;
  159. }
  160. ll ask(int l,int r){
  161. node re0(0,0,0,0),re1(0,0,0,0),rev0(0,0,0,0),rev1(0,0,0,0),tmp;int p=1;
  162. //pos==1: l=l,r=r
  163. //pos==0: l=r,r=l
  164. //这里是因为树剖的过程中会交换lr,所以记录一下,上面的re表示正常算的,rev是因为原式最后还有一个第二种求和,但是方向相反,因此反着的答案也要记录
  165. while(top[l]!=top[r]){
  166. if(dep[top[l]]>dep[top[r]]) swap(l,r),p^=1;
  167. if(!p){
  168. tmp=get1(1,n,pos[top[r]],pos[r],now,0);
  169. merge(re0,re0,tmp);
  170. tmp=get0(1,n,pos[top[r]],pos[r],now,0);
  171. merge(rev0,tmp,rev0);
  172. }
  173. else{
  174. tmp=get0(1,n,pos[top[r]],pos[r],now,0);
  175. merge(re1,tmp,re1);
  176. tmp=get1(1,n,pos[top[r]],pos[r],now,0);
  177. merge(rev1,rev1,tmp);
  178. }
  179. r=fa[top[r]];
  180. }
  181. if(dep[l]>dep[r]) swap(l,r),p^=1;
  182. if(p){
  183. merge(re0,re0,get0(1,n,pos[l],pos[r],now,0));
  184. merge(re1,re0,re1);
  185. merge(rev0,get1(1,n,pos[l],pos[r],now,0),rev0);
  186. merge(rev1,rev1,rev0);
  187. }
  188. else{
  189. merge(re1,get1(1,n,pos[l],pos[r],now,0),re1);
  190. merge(re1,re0,re1);
  191. merge(rev1,rev1,get0(1,n,pos[l],pos[r],now,0));
  192. merge(rev1,rev1,rev0);
  193. }
  194. return (((sqr(re1.siz)+3*re1.siz+2)*inv2%MOD*re1.sum0%MOD)-((2ll*re1.siz+3)*inv2%MOD*re1.sum1%MOD)+(inv2*re1.sum2%MOD)-rev1.sum1+MOD*2)%MOD;
  195. //这里就是统计答案,方法和上面的题解中的公式是一样的
  196. }
  197. void modify(int l,int r,int val){//链上修改
  198. int pre=now;
  199. while(top[l]!=top[r]){
  200. if(dep[top[l]]>dep[top[r]]) swap(l,r);
  201. pre=change(1,n,pos[top[r]],pos[r],pre,val);
  202. r=fa[top[r]];
  203. }
  204. if(dep[l]>dep[r]) swap(l,r);
  205. root[++tot]=change(1,n,pos[l],pos[r],pre,val);
  206. now=root[tot];
  207. }
  208. int main(){
  209. memset(first,-1,sizeof(first));
  210. ll i,t1,t2,t3,tt;ll lastans=0;
  211. Sum2[0]=0;
  212. for(i=1;i<=100000;i++) Sum2[i]=(Sum2[i-1]+i*i)%MOD;
  213. n=read();int Q=read();
  214. for(i=1;i<n;i++){
  215. t1=read(),t2=read();
  216. addedge(t1,t2);
  217. }
  218. for(i=1;i<=n;i++) ori[i]=read();
  219. dfs1(1,0);dfs2(1,1);
  220. now=root[0]=build(1,n);
  221. while(Q--){
  222. tt=read();
  223. if(tt==1){
  224. t1=read();t2=read();t3=read();
  225. t1^=lastans;t2^=lastans;
  226. modify(t1,t2,t3);
  227. }
  228. if(tt==2){
  229. t1=read();t2=read();
  230. t1^=lastans;t2^=lastans;
  231. printf("%lld\n",lastans=ask(t1,t2));
  232. }
  233. if(tt==3){
  234. t1=read();t1^=lastans;
  235. now=root[t1];
  236. }
  237. }
  238. }

[GDOI2016] 疯狂动物园 [树链剖分+可持久化线段树]的更多相关文章

  1. BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector

    题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...

  2. 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流

    [BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...

  3. 【BZOJ4704】旅行 树链剖分+可持久化线段树

    [BZOJ4704]旅行 Description 在Berland,有n个城堡.每个城堡恰好属于一个领主.不同的城堡属于不同的领主.在所有领主中有一个是国王,其他的每个领主都直接隶属于另一位领主,并且 ...

  4. BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

  5. BZOJ 2243:染色(树链剖分+区间合并线段树)

    [SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认 ...

  6. HDU 5029 Relief grain 树链剖分打标记 线段树区间最大值

    Relief grain Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid= ...

  7. 【Codeforces】【网络流】【树链剖分】【线段树】ALT (CodeForces - 786E)

    题意 现在有m个人,每一个人都特别喜欢狗.另外还有一棵n个节点的树. 现在每个人都想要从树上的某个节点走到另外一个节点,且满足要么这个人自带一条狗m,要么他经过的所有边h上都有一条狗. 2<=n ...

  8. LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】

    题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...

  9. HYSBZ 4034 【树链剖分】+【线段树 】

    <题目链接> 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权值.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x ...

随机推荐

  1. centos7部署harbor

    官网 https://github.com/goharbor/harbor 1.升级系统内核 rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrep ...

  2. ES6笔记03-Set和Map数据结构

    ES6提供了新的数据结构Set.它类似于数组,但是成员的值都是唯一的,没有重复的值.Set本身是一个构造函数,用来生成Set数据结构. var s = new Set(); [2, 3, 5, 4, ...

  3. 判断移动端和pc端最简单的方法

    <!DOCTYPE html><html><head> <title></title> <script type="text ...

  4. 裸机——RTC

    1. 首先晓得RTC的基本知识 RTC被划分到timer,但RTC是面向时间点的. 如果按照定时器的思路去思考,那么应该考虑 时间周期 和 计数值. RTC 不是面向时间点的,所以略有不同, 时间周期 ...

  5. [CQOI2007]余数求和 (分块+数学

    题目描述 给出正整数n和k,计算G(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数.例如G(10, 5)=5 ...

  6. 7,MongoDB 之 Limit 选取 Skip 跳过 Sort 排序

    我们已经学过MongoDB的 find() 查询功能了,在关系型数据库中的选取(limit),排序(sort) MongoDB中同样有,而且使用起来更是简单 首先我们看下添加几条Document进来 ...

  7. CSS3裁剪与遮罩解析

    一.用途 CSS3裁剪与遮罩(Clipping and Masking)用来隐藏元素的一部分而显示另一部分 二.区别 CSS3裁剪与遮罩(Clipping and Masking)用来隐藏元素的一部分 ...

  8. 绿盟python测试实习面试

    1.简历问题 低级错误:时间写错 最近好像越来越马大哈了,总是犯低级错误. 上次的开题报告首页,这次的时间,每次都有小问题,确是大毛病 到底哪里出错了 2 RHCE证书好像没有用 面试官根本就不懂这个 ...

  9. 【Java集合源码剖析】Java集合框架

    Java集合工具包位于Java.util包下,包含了很多常用的数据结构,如数组.链表.栈.队列.集合.哈希表等.学习Java集合框架下大致可以分为如下五个部分:List列表.Set集合.Map映射.迭 ...

  10. iOS Crash 分析 符号化崩溃日志

    参考: http://blog.csdn.net/diyagoanyhacker/article/details/41247367 http://blog.csdn.net/diyagoanyhack ...