阅读此篇文章前请先跟我大喊三声: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. C++面向对象的设计思想——小结

    1 对象的概念 面向对象(Object Oriented Analysis Design,OOAD)的思想把整个世界看成是由具有某种特征行为功能的基本单元——对象构成的.OOAD把一个对象的特征称为属 ...

  2. struct和class两个关键字的区别

    1. <C++ Primer> 用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private. 2. ...

  3. Unity 引用的玩家不受控制

    原因是因为从Project拖过去的, 应该从Hierarchy拖过去.

  4. 《Exception》第八次团队作业:Alpha冲刺(大结局)

    一.项目基本介绍 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 Exception 作业学习目标 1.掌握软件测试基础技术.2.学习迭代式增量软 ...

  5. 【BZOJ1396】识别子串 - 后缀自动机+线段树

    题意: Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. 题解: ...

  6. [置顶] openHAB 体系结构与编程模型 (1) --- 术语

    openHAB 术语 Item : 对硬件设备属性的抽象 ( Items are objects that can be read from or written to in order to int ...

  7. HDU 2276 Kiki & Little Kiki 2( 矩阵快速幂 + 循环同构矩阵 )

    蒟蒻的我还需深入学习 链接:传送门 题意:给出一个长度为 n,n 不超过100的 01 串 s ,每当一个数字左侧为 1 时( 0的左侧是 n-1 ),这个数字就会发生改变,整个串改变一次需要 1s ...

  8. S3C6410 LCD Overlay Test Program

    测试了一下S3C6410 LCD控制器提供的Overlay功能,写了一个简单的test,主要用于实现FB0和FB1之间的Overlay操作.内核选项的Windows至少要为2. 具体支持如下操作:1 ...

  9. crm 系统项目(三) 自动分页

    crm 系统项目(三) 自动分页 需求: 1. 做一个自动分页, 每15条数据1页 2. 让当前页数在中间显示 3. 上一页, 下一页 注意情况: 1.总页数 小于 规定显示的页数 2. 左右两边极值 ...

  10. Django -查询数据库相关操作

    一. 内置Admin 依赖APP: django.contrib.auth django.contrib.contenttypes django.contrib.messages django.con ...