传送门: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为根的子树没有区别

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

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

可以通过此题


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

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 200010
#define ll long long
using namespace std;
int i,j,k,m,n,x,y,z,cnt,w;ll now;
int up[][M],F[M],L[M],N[M],a[M],fa[M],size[M],deep[M],v[M],dfn[M],to[M];
int sum[M][];
inline ll read(){
ll x = ; char zf = ; char ch = getchar();
while (ch != '-' && !isdigit(ch)) ch = getchar();
if (ch == '-') zf = -, ch = getchar();
while (isdigit(ch)) x = x * + ch - '', ch = getchar(); return x * zf;
}
void write(ll y){if(y<)putchar('-'),y=-y;if(y>)write(y/);putchar(y%+);}
void writeln(const ll y){write(y);putchar('\n');}
inline void ins(const int x,const int p){
now+=sum[x][p^];
sum[x][p]++;
}
inline void del(const int x,const int p){
now-=sum[x][p^];
sum[x][p]--;
}
struct query{
int L,R,id,zf,p;
inline bool operator <(const query s)const{
return (p==s.p)?(R<s.R):(p<s.p);
}
}q[];
void add(int x,int y){
a[++k]=y;
if(!F[x])F[x]=k;
else N[L[x]]=k;
L[x]=k;
}
void dfs(int x,int pre){
dfn[x]=++cnt;to[cnt]=x;
up[][x]=pre;size[x]=;
for(int i=F[x];i;i=N[i])if(a[i]!=pre){
deep[a[i]]=deep[x]+;
dfs(a[i],x);
size[x]+=size[a[i]];
}
}
int son(int x,int y){//求出x的孩子中是y的祖先的那个孩子
for(int i=,s=deep[y]-deep[x]-;s;i++)if(s>>i&)s^=(<<i),y=up[i][y];
return y;
}
bool in(int x,int y){//判断x是否为y的祖先
return dfn[x]<=dfn[y]&&dfn[x]+size[x]->=dfn[y];
}
void add(int id,int L1,int R1,int L2,int R2){//将其加入莫队询问
q[++cnt]={R1,R2,id,,R1/w};
if(L1>)q[++cnt]={L1-,R2,id,-,(L1-)/w};
if(L2>)q[++cnt]={R1,L2-,id,-,R1/w};
if(L1>&&L2>)q[++cnt]={L1-,L2-,id,,(L1-)/w};
}
struct now{
int L,R;
}aa[],bb[];
ll ans[];
struct w{
int x,id;
inline bool operator <(const w s)const{
return x<s.x;
}
}c[];
int main(){
n=read();m=read();w=;
for(int i=;i<=n;i++)c[i].x=read(),c[i].id=i;
sort(c+,c+n+);int vv=;
for(int i=;i<=n;i++){
if(i==||c[i].x!=c[i-].x)vv++;
v[c[i].id]=vv;
}//离散化
for(int i=;i<n;i++){
x=read();y=read();
add(x,y);
add(y,x);
}
dfs(,);cnt=;
for(int i=;i<=;i++)
for(int j=;j<=n;j++)
up[i][j]=up[i-][up[i-][j]];
int nowroot=,tot=;
for(int i=;i<=m;i++){
int opt=read();
if(opt==){
nowroot=read();
continue;
}
x=read();y=read();tot++;
int cnt1=,cnt2=;
if(x==nowroot)aa[++cnt1]={,n};
else if(in(x,nowroot)){
int pl=son(x,nowroot);
aa[++cnt1]={,dfn[pl]-};
aa[++cnt1]={dfn[pl]+size[pl],n};
}
else aa[++cnt1]={dfn[x],dfn[x]+size[x]-}; if(y==nowroot)bb[++cnt2]={,n};
else if(in(y,nowroot)){
int pl=son(y,nowroot);
bb[++cnt2]={,dfn[pl]-};
bb[++cnt2]={dfn[pl]+size[pl],n};
}
else bb[++cnt2]={dfn[y],dfn[y]+size[y]-};
//aa和bb存询问点x,y对应的区间
ll ans=;
for(int q1=;q1<=cnt1;q1++)
for(int q2=;q2<=cnt2;q2++)
add(tot,aa[q1].L,aa[q1].R,bb[q2].L,bb[q2].R);//加入询问
}
sort(q+,q+cnt+);
int L=,R=;now=;
for(int i=;i<=cnt;i++){
while(L<q[i].L)ins(v[to[++L]],);
while(L>q[i].L)del(v[to[L--]],);
while(R<q[i].R)ins(v[to[++R]],);
while(R>q[i].R)del(v[to[R--]],);
ans[q[i].id]+=now*q[i].zf;//莫队计算
}
for(int i=;i<=tot;i++)writeln(ans[i]);
return ;
}

[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. JDK和CGLIB动态代理区别

    背景:虽然自己了解这两种代理的区别,但是面试时候还是答的很模糊,需要好好总结. 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/de ...

  2. Let's Encrypt:初次使用免费的ssl证书,并生成java用的 jks(keystore) 文件

    现在都流行 https,今天晚上花了二个小时,学习了一下,这里做个学习总结: 因为刚开始接触,就使用免费的:Let's Encrypt Let's Encrypt证书特点: 1. 现在主流的浏览器(c ...

  3. python基础-守护进程、守护线程、守护非守护并行

    守护进程 1.守护子进程 主进程创建守护进程  其一:守护进程会在主进程代码执行结束后就终止  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic pro ...

  4. vcftools报错:Writing PLINK PED and MAP files ... Error: Could not open temporary file.解决方案

    一般来说有两种解决方案. 第一种:添加“--plink-tped”参数: 用vcftools的“--plink”参数生成plink格式文件时,小样本量测试可以正常生成plink格式,用大样本量时产生W ...

  5. 第三十五节,目标检测之YOLO算法详解

    Redmon, J., Divvala, S., Girshick, R., Farhadi, A.: You only look once: Unified, real-time object de ...

  6. HTML学习笔记Day2

    一.部分表单元素的使用 1.表单的作用:用来收集用户信息 2.表单元素 (1)表单控件: 单行文本框:<input  type="text" value="默认值& ...

  7. 应用实战:从Redis到Aerospike,我们踩了这些坑

    个推专注为开发者们提供消息推送服务多年.通过个推SDK,手机终端与服务器建立长连接,维持在线状态.然而在网络异常等情况下,消息无法实时送达到终端用户,因而推送服务器建立了一份离线消息列表,以待用户重新 ...

  8. 数据库MySQL

    --IN 关键字 在.....里 SELECT * FROM zhangwu WHERE money IN (66,666,700); 1.主键约束 特点非空 只用于表示當前的记录 primary k ...

  9. C# 实现身份验证之WebApi篇

    今天再来总结关于如何实现WebApi的身份验证,以完成该系列所有文章,WebApi常见的实现方式有:FORM身份验证.集成WINDOWS验证.Basic基础认证.Digest摘要认证  第一种:FOR ...

  10. 互联网运营+SEO:推荐必看的5本书籍

    本文首发于:风云社区(scoee.com) 最近开始学习和研究互联网运营和SEO,对于我这个小白来讲,还是有些吃力,毕竟从来没接触这方面的,尽管在之前的软件公司做过售前和产品相关的工作,但毕竟与互联网 ...