阅读此篇文章前请先跟我大喊三声:dllxl!dllxl!dllxl!

咳咳。

题意:

Description

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

Input

第一行两个数表示 n,m。
第二行 n 个数,表示每个点的点权 a[i]。
之后 n - 1 行 , 每行两个数 x , y , 表示一条边
之后 m 行,每行为 1 x 或者 2 x y。
1 x,表示将根变成 x点。
2 x y,表示查询 x 点的子树与 y 点的子树。
n <= 100000 , m <= 500000 , 1 <= a[i] <= 1000000000

Output

对于每个询问,输出一个数表示答案。

题解:

非常不推荐去阅读原题面(看完题面就知道出题人系列)

众所周知,云南省选全是毒瘤数据结构题

其实这是一道BZOJ二合一。。。BZOJ3083 遥远的国度+BZOJ5016一个简单的询问(题解咕咕咕中)

如果没有换根操作,先来考虑如何处理询问(BZOJ5016):

首先按照dfs序把问题转化到序列上,询问就变成了选取序列中的两个区间,答案即为在两个区间中出现过的每个数分别在两个区间出现次数的乘积;

用公式表达就是:

$ans=\sum\limits_{x=1}^{\infty}sum(l_1,r_1,x)\times sum(l_2,r_2,x)$,其中$sum(l,r,x)$表示数字$x$在区间$[l,r]$中出现的次数。

这个东西。。。不好处理啊。。。

一般这种多组询问且不知所云不好维护的东西都会想到分块,然后快(du)乐(liu)莫队?

那就推一推前缀形式:

$ans=\sum\limits_{x=1}^{\infty}sum(l_1,r_1,x)\times sum(l_2,r_2,x)$

$=\sum\limits_{x=1}^{\infty}(sum(1,r_1,x)-sum(1,l_1-1,x))\times(sum(1,r_2,x)-sum(1,l_2-1,x))$

$=\sum\limits_{x=1}^{\infty}sum(1,r_1,x)·sum(1,r_2,x)-sum(1,r_1,x)·sum(1,l_2-1,x)-sum(1,l_1-1,x)·sum(1,r_2,x)+sum(1,l_1-1,x)·sum(1,l_2-1,x)$

设$f(n,m)=\sum\limits_{x=1}^{\infty}sum(1,n,x)·sum(1,m,x)$

则$ans=f(r_1,r_2)-f(r_1,l_2-1)-f(l_1-1,r_2)+f(l_1-1,l_2-1)$

所以可以询问一拆四然后直接上莫队……

然后看换根(BZOJ3083):

数据结构学傻了->这题一定能用LCT!

啊LCT不能维护子树信息……

数据结构学傻了*2->换根一定能用树剖记录!

啊dfs序会变然后就不能维护了……

因此我们要大力分类讨论:

设根为root,所求子树的根为rt

1.root==rt:continue;

2.root不在rt的子树中:即换根后rt子树中的dfs顺序不会变,所以直接遍历即可;

3.rt在root的子树中:

分析此时的dfs顺序:应该是从root开始,访问到rt的子节点中是root的祖先的节点(记为点F),再访问rt,最后才是其它节点。那么在rt之前访问到的只有F的子树,其它的都是rt子树中的节点。众所周知子树dfs序连续,所以查询rt此时的子树就相当于查询线段树中$[1,l-1]$和$[r+1,n]$中的节点。

所以只有第三种情况特殊求一个F,然后直接维护即可;

求点F可以用树上倍增,当然在bzoj3083原题中是树剖就可以用树剖找。

然后就可以喜闻乐见的把两题拼在一起,码码码~

要注意的是由于换根的三种情况比较麻烦,莫队要拆成九个询问,于是常数upup

最后的时间复杂度是$O(n\sqrt{m})$,原题由于友(du)善(liu)出题人lxl的无私馈赠因此要大力卡常+调参+fread、fwrite才能过,而小视野非常良心的开了两倍时限,于是就开心过掉啦~

代码:

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
#define eps 1e-9
using namespace std;
//typedef long long ll;
struct edge{
int v,next;
}a[];
struct nd{
int x,id;
friend bool operator <(nd a,nd b){
return a.x<b.x;
}
}s[];
int BLK,n,m,op,u,v,l,r,rt=,tot=,tim=,cnt=,tmp=,head[],ans[],pos[],cl[],ll[],rr[],num[],in[],out[],dep[],fa[][];
bool isans[];
struct task{
int l,r,op,id;
friend bool operator <(task a,task b){
return pos[a.l]==pos[b.l]?pos[a.r]<pos[b.r]:pos[a.l]<pos[b.l];
}
}t[];
void add(int u,int v){
a[++tot].v=v;
a[tot].next=head[u];
head[u]=tot;
}
void dfs(int u,int ff,int dpt){
in[u]=++tim;
dep[u]=dpt;
fa[u][]=ff;
for(int i=;i<=;i++)fa[u][i]=fa[fa[u][i-]][i-];
for(int tmp=head[u];tmp!=-;tmp=a[tmp].next){
int v=a[tmp].v;
if(v!=ff){
dfs(v,u,dpt+);
}
}
out[u]=tim;
}
/*int lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
int l=dep[u]-dep[v];
for(int i=19;i>=0;i--){
if((1<<i)&l)u=fa[u][i];
}
if(u==v)return u;
for(int i=19;i>=0;i--){
if(fa[u][i]!=fa[v][i]){
u=fa[u][i],v=fa[v][i];
}
}
return fa[u][0];
}*/
int get(int u){
return (dep[u]<dep[rt]&&in[u]<=in[rt]&&in[rt]<=out[u])?u:rt;
}
int getfa(int u,int l){
for(int i=;i>=;i--){
if((<<i)&l)u=fa[u][i];
}
return u;
}
void mv1l(int x){
tmp+=rr[cl[x]];
ll[cl[x]]++;
}
void mv2l(int x){
tmp-=rr[cl[x]];
ll[cl[x]]--;
}
void mv1r(int x){
tmp+=ll[cl[x]];
rr[cl[x]]++;
}
void mv2r(int x){
tmp-=ll[cl[x]];
rr[cl[x]]--;
}
int main(){
memset(head,-,sizeof(head));
memset(isans,,sizeof(isans));
scanf("%d%d",&n,&m);
BLK=(int)sqrt(n);
for(int i=;i<=n;i++)pos[i]=(i-)/BLK+;
for(int i=;i<=n;i++){
scanf("%d",&s[i].x);
s[i].id=i;
}
for(int i=;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs(,,);
sort(s+,s+n+);
for(int i=;i<=n;i++){
if(s[i].x!=s[i-].x)cnt++;
cl[in[s[i].id]]=cnt;
}
cnt=;
for(int i=;i<=m;i++){
scanf("%d%d",&op,&u);
if(op==)rt=u;
else{
scanf("%d",&v);
isans[i]=true;
if(u==v&&u==rt){
t[++cnt]=(task){n,n,,i};
continue;
}
if(u!=v&&v==rt)swap(u,v);
int fu=get(u),fv=get(v);
if(u!=v&&u==rt){
if(fv==v){
v=getfa(rt,dep[rt]-dep[v]-);
t[++cnt]=(task){n,in[v]-,,i};
t[++cnt]=(task){n,n,,i};
t[++cnt]=(task){n,out[v],-,i};
}else{
t[++cnt]=(task){n,out[v],,i};
t[++cnt]=(task){n,in[v]-,-,i};
}
continue;
}
if(fu==u&&fv!=v){
swap(u,v);
swap(fu,fv);
}
if(fu!=u&&fv!=v){
t[++cnt]=(task){out[u],out[v],,i};
t[++cnt]=(task){out[u],in[v]-,-,i};
t[++cnt]=(task){in[u]-,in[v]-,,i};
t[++cnt]=(task){in[u]-,out[v],-,i};
}else if(fu!=u&&fv==v){
v=getfa(rt,dep[rt]-dep[v]-);
t[++cnt]=(task){out[u],in[v]-,,i};
t[++cnt]=(task){out[u],n,,i};
t[++cnt]=(task){out[u],out[v],-,i};
t[++cnt]=(task){in[u]-,in[v]-,-,i};
t[++cnt]=(task){in[u]-,n,-,i};
t[++cnt]=(task){in[u]-,out[v],,i};
}else{
u=getfa(rt,dep[rt]-dep[u]-);
v=getfa(rt,dep[rt]-dep[v]-);
t[++cnt]=(task){n,in[v]-,,i};
t[++cnt]=(task){n,n,,i};
t[++cnt]=(task){n,out[v],-,i};
t[++cnt]=(task){out[u],in[v]-,-,i};
t[++cnt]=(task){out[u],n,-,i};
t[++cnt]=(task){out[u],out[v],,i};
t[++cnt]=(task){in[u]-,in[v]-,,i};
t[++cnt]=(task){in[u]-,n,,i};
t[++cnt]=(task){in[u]-,out[v],-,i};
}
}
}
sort(t+,t+cnt+);
l=t[].l,r=t[].r;
for(int i=;i<=l;i++)ll[cl[i]]++;
for(int i=;i<=r;i++){
rr[cl[i]]++;
tmp+=ll[cl[i]];
}
ans[t[].id]+=tmp*t[].op;
for(int i=;i<=cnt;i++){
while(l<t[i].l)mv1l(++l);
while(l>t[i].l)mv2l(l--);
while(r<t[i].r)mv1r(++r);
while(r>t[i].r)mv2r(r--);
ans[t[i].id]+=tmp*t[i].op;
}
for(int i=;i<=m;i++){
if(isans[i])printf("%d\n",ans[i]);
}
return ;
}

【BZOJ4940】【YNOI2016】这是我自己的发明的更多相关文章

  1. bzoj4940: [Ynoi2016]这是我自己的发明

    用dfs序把询问表示成询问dfs序的两个区间中的信息 拆成至多9个询问(询问dfs序的两个前缀),对这些询问用莫队处理,时间复杂度$O(n\sqrt{m})$ #include<bits/std ...

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

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

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

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

  4. 【bzoj4940】这是我自己的发明

    Portal --> bzoj4940 Solution (原题这题面到底是..怎么回事啊深深的套路qwq) 感觉自己对根号的算法还是很..没有感觉啊== 实际上这题和bzoj5016没有任何区 ...

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

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

  6. [Ynoi2016]这是我自己的发明 莫队

    传送门:here 很棒的莫队题啊..... 题意: 有一棵$ n$个点的树,树上每个点有点权,有$ m$次询问: 操作1:给定两个点$ x,y$,求二元组$ (a,b)$的数量,要求$ a$在$ x$ ...

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

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

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

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

  9. YNOI2016 这是我自己的发明

    看到这个标题立刻想到:. “绝地科学家,八倍不屏息啊,八百里外把头打啊...” 首先我们发现如果只考虑第二个操作,这棵树就是假的,我们可以直接莫队解决 如果考虑换根的话...可以把一个操作换成小于等于 ...

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

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

随机推荐

  1. Unity 围绕X、Y、Z旋转图例

    绿色:绕X 红色:绕Y 蓝色:绕Z PS:这是右手坐标系,Unity为左手坐标系 不知道啥叫左手右手?参见我的另一篇文章http://www.cnblogs.com/36bian/p/7571727. ...

  2. webpack——entry,output,plugins,loader,chunk知识

    entry:打包入口 代码的入口,找到依赖模块 打包的入口,webpack去哪个文件找依赖,或是去那个文件依赖的依赖 可以是一个或者多个入口 例如: 1.module.exports={ entry: ...

  3. git--客户端管理工具初步使用

    说点废话哈 小白一枚, 今年3月份进入自己的第一家公司, 开始成为前端中的一份子,好在公司里有位和我一同进来的一位老哥带着我,从老哥身上学到的知识不多,(因为和老哥只相处工作了三个月,因为家里的事情, ...

  4. wordpress 拾遗

    wordpress 拾遗 运行环境 php mySQL Apache 集成开发环境 Appserv xampp phpstudy 文章和页面的区别 文章是发布网站主要内容的地方,比如博客的文章,商城的 ...

  5. [读书笔记] R语言实战 (五) 高级数据管理

    1. 数值函数 1) 数学函数 2) 统计函数 3. 数据标准化 scale() 函数对矩阵或者数据框的指定列进行均值为0,标准化为1的标准化 mydata <- data.frame(c1=c ...

  6. Win10和子系统Ubuntu简单共享

    Win10和子系统Ubuntu简单共享 C:\Users\用户名\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp ...

  7. 原生ajax的请求过程

    原生ajax的请求过程 创建全平台兼容的XMLHttpRequest对象: function getXHR(){ var xhr = null; if(window.XMLHttpRequest) { ...

  8. Vue入门教程(2)

    小白入门学习vue和vue实例,vue总结 这就是我脑海中的 Vue 知识体系: 一句话概况了 Vue 通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件 Vue 的创建 我们的学习目的肯定 ...

  9. Eclipse下的java工程目录问题和路径问题理解

    1.Eclipse下的java工程都有哪些文件夹? 答:new java project时,会默认创建SRC源代码目录,并默认创建一个bin目录作为输出目录,输出目录是指生成的class文件和配置文件 ...

  10. Chrome的JSON View插件

    Chrome的JSON View插件 学习了:http://www.cnplugins.com/zhuanti/five-chrome-json-plugins.html 下载了:http://www ...