传送门:here

很棒的莫队题啊.....


题意:

有一棵$ n$个点的树,树上每个点有点权,有$ m$次询问:

操作1:给定两个点$ x,y$,求二元组$ (a,b)$的数量,要求$ a$在$ x$的子树内,$ b$在$ y$的子树内,且$ a$和$ b$的权值相同

操作2:给定点$ x$,将根节点换成$ x$


$ solution:$

我们先考虑没有换根操作

我们先求出这棵树所有点的dfs序,然后可以把树上问题转化成区间询问

$ \sum\limits_{i=L1}^{R1}\sum\limits_{j=L2}^{R2}[ value[i]=value[j] ]$

会发现这就是LOJ2254

我们令$ g(x,L,R)$表示区间$ [L,R]$中x的数量

则原式等价于$ \sum\limits_{x}g(x,L1,R1)g(x,L2,R2)$

化成前缀相减的形式:$ \sum\limits_{x}(g(x,1,R1)-g(x,1,L1-1))(g(x,1,R2)-g(x,L2-1,R2))$

令$ f(a,b)=\sum\limits_{i=1}^{a}\sum\limits_{j=1}^{b}[ value[i]=value[j] ]$

会发现可以化简成$ f(R1,R2)-f(L1-1,R2)-f(R1,L2-1)+f(L1-1,L2-1)$

对于每个$ f(a,b)$,我们记录它的符号以及在第几个询问里,然后可以直接挂在莫队上跑


然后考虑如果有换根操作

我们假定原先的根为$ 1$

然后假设将根换成了$ x$并且询问$ k$的统治区间

我们分三类讨论

$ 1.k=x$

此时子树范围为整个区间

$ 2.k$在$1...x$的路径上

显然$ k$是$ x$的祖先

找出$ k$的孩子中也是$ x$的祖先的节点

设这个节点在以一号点为根时的子树范围为$ [L,R]$

则此时$ k$的统治范围为$ [1,L-1]并上[R+1,n]$

$ 3. othercase $

此时子树和以1为根的子树没有区别

这样我们将每次询问拆成若干区间

然后用上面没有换根的时候的方法扔进莫队暴力计算

可以通过此题


附上我的代码(人菜自带大常数)

  1. #include<ctime>
  2. #include<cmath>
  3. #include<cstdio>
  4. #include<cstring>
  5. #include<iostream>
  6. #include<algorithm>
  7. #define M 200010
  8. #define ll long long
  9. using namespace std;
  10. int i,j,k,m,n,x,y,z,cnt,w;ll now;
  11. int up[][M],F[M],L[M],N[M],a[M],fa[M],size[M],deep[M],v[M],dfn[M],to[M];
  12. int sum[M][];
  13. inline ll read(){
  14. ll x = ; char zf = ; char ch = getchar();
  15. while (ch != '-' && !isdigit(ch)) ch = getchar();
  16. if (ch == '-') zf = -, ch = getchar();
  17. while (isdigit(ch)) x = x * + ch - '', ch = getchar(); return x * zf;
  18. }
  19. void write(ll y){if(y<)putchar('-'),y=-y;if(y>)write(y/);putchar(y%+);}
  20. void writeln(const ll y){write(y);putchar('\n');}
  21. inline void ins(const int x,const int p){
  22. now+=sum[x][p^];
  23. sum[x][p]++;
  24. }
  25. inline void del(const int x,const int p){
  26. now-=sum[x][p^];
  27. sum[x][p]--;
  28. }
  29. struct query{
  30. int L,R,id,zf,p;
  31. inline bool operator <(const query s)const{
  32. return (p==s.p)?(R<s.R):(p<s.p);
  33. }
  34. }q[];
  35. void add(int x,int y){
  36. a[++k]=y;
  37. if(!F[x])F[x]=k;
  38. else N[L[x]]=k;
  39. L[x]=k;
  40. }
  41. void dfs(int x,int pre){
  42. dfn[x]=++cnt;to[cnt]=x;
  43. up[][x]=pre;size[x]=;
  44. for(int i=F[x];i;i=N[i])if(a[i]!=pre){
  45. deep[a[i]]=deep[x]+;
  46. dfs(a[i],x);
  47. size[x]+=size[a[i]];
  48. }
  49. }
  50. int son(int x,int y){//求出x的孩子中是y的祖先的那个孩子
  51. for(int i=,s=deep[y]-deep[x]-;s;i++)if(s>>i&)s^=(<<i),y=up[i][y];
  52. return y;
  53. }
  54. bool in(int x,int y){//判断x是否为y的祖先
  55. return dfn[x]<=dfn[y]&&dfn[x]+size[x]->=dfn[y];
  56. }
  57. void add(int id,int L1,int R1,int L2,int R2){//将其加入莫队询问
  58. q[++cnt]={R1,R2,id,,R1/w};
  59. if(L1>)q[++cnt]={L1-,R2,id,-,(L1-)/w};
  60. if(L2>)q[++cnt]={R1,L2-,id,-,R1/w};
  61. if(L1>&&L2>)q[++cnt]={L1-,L2-,id,,(L1-)/w};
  62. }
  63. struct now{
  64. int L,R;
  65. }aa[],bb[];
  66. ll ans[];
  67. struct w{
  68. int x,id;
  69. inline bool operator <(const w s)const{
  70. return x<s.x;
  71. }
  72. }c[];
  73. int main(){
  74. n=read();m=read();w=;
  75. for(int i=;i<=n;i++)c[i].x=read(),c[i].id=i;
  76. sort(c+,c+n+);int vv=;
  77. for(int i=;i<=n;i++){
  78. if(i==||c[i].x!=c[i-].x)vv++;
  79. v[c[i].id]=vv;
  80. }//离散化
  81. for(int i=;i<n;i++){
  82. x=read();y=read();
  83. add(x,y);
  84. add(y,x);
  85. }
  86. dfs(,);cnt=;
  87. for(int i=;i<=;i++)
  88. for(int j=;j<=n;j++)
  89. up[i][j]=up[i-][up[i-][j]];
  90. int nowroot=,tot=;
  91. for(int i=;i<=m;i++){
  92. int opt=read();
  93. if(opt==){
  94. nowroot=read();
  95. continue;
  96. }
  97. x=read();y=read();tot++;
  98. int cnt1=,cnt2=;
  99. if(x==nowroot)aa[++cnt1]={,n};
  100. else if(in(x,nowroot)){
  101. int pl=son(x,nowroot);
  102. aa[++cnt1]={,dfn[pl]-};
  103. aa[++cnt1]={dfn[pl]+size[pl],n};
  104. }
  105. else aa[++cnt1]={dfn[x],dfn[x]+size[x]-};
  106.  
  107. if(y==nowroot)bb[++cnt2]={,n};
  108. else if(in(y,nowroot)){
  109. int pl=son(y,nowroot);
  110. bb[++cnt2]={,dfn[pl]-};
  111. bb[++cnt2]={dfn[pl]+size[pl],n};
  112. }
  113. else bb[++cnt2]={dfn[y],dfn[y]+size[y]-};
  114. //aa和bb存询问点x,y对应的区间
  115. ll ans=;
  116. for(int q1=;q1<=cnt1;q1++)
  117. for(int q2=;q2<=cnt2;q2++)
  118. add(tot,aa[q1].L,aa[q1].R,bb[q2].L,bb[q2].R);//加入询问
  119. }
  120. sort(q+,q+cnt+);
  121. int L=,R=;now=;
  122. for(int i=;i<=cnt;i++){
  123. while(L<q[i].L)ins(v[to[++L]],);
  124. while(L>q[i].L)del(v[to[L--]],);
  125. while(R<q[i].R)ins(v[to[++R]],);
  126. while(R>q[i].R)del(v[to[R--]],);
  127. ans[q[i].id]+=now*q[i].zf;//莫队计算
  128. }
  129. for(int i=;i<=tot;i++)writeln(ans[i]);
  130. return ;
  131. }

[Ynoi2016]这是我自己的发明 莫队的更多相关文章

  1. 洛谷P4689 [Ynoi2016]这是我自己的发明 [莫队]

    传送门 ynoi中比较良心不卡常的题. 思路 没有换根操作时显然可以变成dfs序莫队随便搞. 换根操作时一个子树可以变成两段区间的并集,也随便搞搞就好了. 这题完全不卡常,随便过. 代码 #inclu ...

  2. bzoj4940 [Ynoi2016]这是我自己的发明 莫队+dfs序

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4940 题解 对于换根操作,处理方法就很套路了. 首先先假定以 \(1\) 为根做一遍 dfs, ...

  3. 【洛谷 P4688】 [Ynoi2016]掉进兔子洞(bitset,莫队)

    题目链接 第一道Ynoi 显然每次询问的答案为三个区间的长度和减去公共数字个数*3. 如果是公共数字种数的话就能用莫队+bitset存每个区间的状态,然后3个区间按位与就行了. 但现在是个数,bits ...

  4. 洛谷P4689 [Ynoi2016]这是我自己的发明(莫队,树的dfn序,map,容斥原理)

    洛谷题目传送门 具体思路看别的题解吧.这里只提两个可能对常数和代码长度有优化的处理方法. I 把一个询问拆成\(9\)个甚至\(16\)个莫队询问实在是有点珂怕. 发现询问的一边要么是一个区间,要么是 ...

  5. [Ynoi2016]这是我自己的发明(莫队)

    话说这道题数据是不是都是链啊,我不手动扩栈就全 \(RE\)... 不过 \(A\) 了这题还是很爽的,通过昨晚到今天早上的奋斗,终于肝出了这题 其实楼上说的也差不多了,就是把区间拆掉然后莫队瞎搞 弱 ...

  6. 【bzoj4940】[Ynoi2016]这是我自己的发明 DFS序+树上倍增+莫队算法

    题目描述 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. 2. 给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等, ...

  7. Luogu4689 [Ynoi2016]这是我自己的发明 【莫队】

    题目链接:洛谷 又来做Ynoi里面的水题了... 首先换根的话是一个套路,首先以1为根dfs,然后画一画就知道以rt为根,x的子树是什么了.可以拆分为2个dfs连续段. 然后如果要计算\([l_1,r ...

  8. 洛谷P4689 [Ynoi2016]这是我自己的发明(树上莫队+树链剖分)

    题目描述 您正在打galgame,然后突然家长进来了,于是您假装在写数据结构题: 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1.将树根换为 x. 2.给出两个点 x,y,从  ...

  9. luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队

    题目链接 luogu P4688 [Ynoi2016]掉进兔子洞 题解 莫队维护bitset区间交个数 代码 // luogu-judger-enable-o2 #include<cmath&g ...

随机推荐

  1. python 装饰器的应用

    import time def test1(): print "hello\n" print test1.__name__ def test2(): print "hel ...

  2. JS怎么判断一个对象是否为空

    昨天面试的时候被问到的问题.只怪自己根基不牢,没有回答好 甚至说出了“判断这个obj是否和{}相等”这样鱼蠢的答案(/(ㄒoㄒ)/~~)引用类型怎么可以直接判断==或者===呢?! 今天中秋佳节,宝宝 ...

  3. POJ 1815 Friendship (Dinic)

    Friendship Time Limit: 2000MS   Memory Limit: 20000K Total Submissions: 11429   Accepted: 3173 Descr ...

  4. java linux sdk1.8

    wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-co ...

  5. java中Comparatable接口和Comparator接口的区别

    1.不同类型的排序规则 .自然排序是什么?   自然排序是一种升序排序.对于不同的数据类型,升序规则不一样:   BigDecimal BigInteger Byte Double Float Int ...

  6. (DFS)P1605 迷宫 洛谷

    题目背景 迷宫 [问题描述] 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和 终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫 中移动有上下 ...

  7. (Bash博弈 大数) 51nod1068 Bash游戏 V3

    1068 Bash游戏 V3   有一堆石子共有N个.A B两个人轮流拿,A先拿.每次拿的数量只能是2的正整数次幂,比如(1,2,4,8,16....),拿到最后1颗石子的人获胜.假设A B都非常聪明 ...

  8. 根据指定的key,将二维数组的value转换为string,适用于mysql的in查询

    function array_unique_join($arr,$param){ $utm_source_arr = array_unique(array_column($arr,$param)); ...

  9. 写给IT技术爱好者的一封信

    写给IT技术爱好者的一封信>当前运维素质的分析<... ---------------------- 虽相貌平平,但勤学苦练,亦收获颇丰!如果你决定要成为一名IT从业者,你需要承受以下的东 ...

  10. 如何在springcloud分布式系统中实现分布式锁?

    一.简介 一般来说,对数据进行加锁时,程序先通过acquire获取锁来对数据进行排他访问,然后对数据进行一些列的操作,最后需要释放锁.Redis 本身用 watch命令进行了加锁,这个锁是乐观锁.使用 ...