阅读此篇文章前请先跟我大喊三声: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. Cacti部署之配置防火墙

    因为SNMP协议通信会使用udp 的161端口和tcp的199端口,因此需要在防火墙上将其开启 开启入站端口   保存数据或者重启服务   测试SNMP工具连接是否可获取信息     注意:要能使用s ...

  2. Smalltalk

    Smalltalk is an object-oriented, dynamically typed, reflective programming language. Smalltalk was o ...

  3. jquery的animate能渐变background-color

    在freecodecamp 上学习复习时,写了一个demo,引用了 jquery 实现 color 的渐变动画,然后运行测试的时候,发现其他功能都正常,就是无法是实现颜色的动画. 如: $('butt ...

  4. 第六章 Python之迭代器与生成器

    迭代器 迭代:迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果是下一次重复的初始值 l=['a','b','c'] count=0 while count < len(l): pri ...

  5. Qwiklab'实验-DynamoDB, Redshift, Elasticsearch'

    title: AWS之Qwiklab subtitle: 4. Qwiklab'实验-Amazon DynamoDB, Amazon Redshift, Elasticsearch Service' ...

  6. Java 实现简单的RPC框架

    0 引言 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).He ...

  7. How Google Backs Up The Internet Along With Exabytes Of Other Data

    出处:http://highscalability.com/blog/2014/2/3/how-google-backs-up-the-internet-along-with-exabytes-of- ...

  8. Laravel核心解读--Contracts契约

    Contracts Laravel 的契约是一组定义框架提供的核心服务的接口, 例如我们在介绍用户认证的章节中到的用户看守器契约IllumninateContractsAuthGuard 和用户提供器 ...

  9. strlen()函数对一个未初始化数组的处理

    今天使用strlen时 ,发现一个问题,demo代码如下: #include <stdio.h> #include <stdlib.h> #include <string ...

  10. linux 上安装 redis

    一.安装gcc Redis是c语言开发的. 安装 redis 需要 c 语言的编译环境.如果没有 gcc 需要在线安装. yum install gcc-c++ 二.下载 redis 链接:https ...