题目描述

您正在打galgame,然后突然家长进来了,于是您假装在写数据结构题:

给一个树,n 个点,有点权,初始根是 1。

m 个操作,每次操作:

1.将树根换为 x。

2.给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等,ans++,求 ans。

题解

  lxl的大毒瘤题名不虚传

  顺便先膜一下gxz大佬再说(毕竟像我这种菜鸡根本想不出这么巧的方法)->这里

  首先,如果没有换根的话,那么可以直接把子树当成dfs序上的一段区间来做,那么只要把询问给拆成好几个询问,然后直接用莫队就可以了

  然后考虑换根要怎么解决呢?可以参考【bzoj3083】遥远的国度,把子树变成原来的dfs序中的最多两段区间

  然后考虑块的大小,大佬说这题卡常,得把块的大小调成$\frac{n}{\sqrt{m}}*2$,否则会被卡,然而亲测不用乘2也能过……虽然跑得稍微慢了点

  顺带提一句,这里的莫队范围是$(1,x)$而不是$(l,r)$(因为是把询问写成了前缀和相减的形式),当初刚看到的时候没看懂还写错了……

 //minamoto
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(ll x){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=;
int a[N],b[N],head[N],Next[N<<],ver[N<<],tot;
int dep[N],ls[N],rs[N],son[N],sz[N],top[N],fa[N],cnt;
int vx[],ox[],tx,vy[],oy[],ty,cx[N],cy[N],w[N],px,py;
ll ans[N*],now;
int n,m,rt,cq,num=;
struct node{
int l,r,rt,id,opt;
node(){}
node(int L,int R,int Id,int Opt){l=min(L,R),r=max(L,R),id=Id,opt=Opt;}
bool operator<(const node &a)const {return rt == a.rt ? r < a.r : l < a.l;}
//inline bool operator <(const node &b)const{return rt==b.rt?rt&1?r<b.r:r>b.r:rt<b.rt;}
}q[N*];
inline void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
}
void dfs1(int u){
sz[u]=,dep[u]=dep[fa[u]]+;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=fa[u]){
fa[v]=u,dfs1(v),sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u,int t){
top[u]=t,ls[u]=++cnt,w[cnt]=a[u];
if(son[u]){
dfs2(son[u],t);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
rs[u]=cnt;
}
inline int find(int t,int u){
while(top[u]!=top[t]){
if(fa[top[u]]==t) return top[u];
u=fa[top[u]];
}
return son[t];
}
int main(){
//freopen("testdata.in","r",stdin);
n=read(),m=read();
for(int i=;i<=n;++i) a[i]=b[i]=read();
sort(b+,b++n);
for(int i=;i<=n;++i) a[i]=lower_bound(b+,b++n,a[i])-b;
for(int i=;i<n;++i){
int u=read(),v=read();add(u,v);
}
dfs1(),dfs2(,);
for(int i=;i<=m;++i){
int op=read(),x=read(),y,z;
if(op&) rt=x;
else{
y=read(),++cq;
tx=ty=;
if(x==rt) vx[++tx]=n,ox[tx]=;
else if(ls[rt]<ls[x]||ls[rt]>rs[x]) vx[++tx]=rs[x],ox[tx]=,vx[++tx]=ls[x]-,ox[tx]=-;
else z=find(x,rt),vx[++tx]=n,ox[tx]=,vx[++tx]=rs[z],ox[tx]=-,vx[++tx]=ls[z]-,ox[tx]=;
if(y==rt) vy[++ty]=n,oy[ty]=;
else if(ls[rt]<ls[y]||ls[rt]>rs[y]) vy[++ty]=rs[y],oy[ty]=,vy[++ty]=ls[y]-,oy[ty]=-;
else z=find(y,rt),vy[++ty]=n,oy[ty]=,vy[++ty]=rs[z],oy[ty]=-,vy[++ty]=ls[z]-,oy[ty]=;
for(int j=;j<=tx;++j)
for(int k=;k<=ty;++k)
if(vx[j]&&vy[k])
q[++num]=node(vx[j],vy[k],cq,ox[j]*oy[k]);
}
}
int s=(n/sqrt(num))*+;
for(int i=;i<=num;++i) q[i].rt=(q[i].l-)/s;
sort(q+,q++num);
for(int i=;i<=num;++i){
while(px<q[i].l) now+=cy[w[++px]],++cx[w[px]];
while(py<q[i].r) now+=cx[w[++py]],++cy[w[py]];
while(px>q[i].l) --cx[w[px]],now-=cy[w[px--]];
while(py>q[i].r) --cy[w[py]],now-=cx[w[py--]];
ans[q[i].id]+=now*q[i].opt;
}
for(int i=;i<=cq;++i) print(ans[i]);
Ot();
return ;
}

洛谷P4689 [Ynoi2016]这是我自己的发明(树上莫队+树链剖分)的更多相关文章

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

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

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

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

  3. 洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

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

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

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

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

  6. 洛谷P3178 树上操作 [HAOI2015] 树链剖分

    正解:树链剖分+线段树 解题报告: 传送门! 树链剖分+线段树算是基操了趴,,, 就无脑码码码,没有任何含金量,不需要动脑子,然后码量其实也不大,就很爽 比树剖的板子还要板子一些hhhhh 放下代码就 ...

  7. 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...

  8. 【洛谷1494】[国家集训队] 小Z的袜子(莫队)

    点此看题面 大致题意: 有\(N\)只从\(1\sim N\)编号的袜子,告诉你每只袜子的颜色,\(M\)组询问,每组询问给你一个区间\([L\sim R]\),让你求出小Z随机抽出\(2\)只袜子时 ...

  9. 洛谷P3178 [HAOI2015]树上操作 题解 树链剖分+线段树

    题目链接:https://www.luogu.org/problem/P3178 这道题目是一道树链剖分的模板题. 但是在解决这道问题的同事刷新了我的两个认识: 第一个认识是:树链剖分不光可以处理链, ...

随机推荐

  1. [LuoguP2157][SDOI2009]学校食堂_状压dp

    学校食堂 题目链接:https://www.luogu.org/problem/P2157 数据范围:略. 题解: 发现$B$特别小,很容易想到状压. 即在$dp$的时候弄出来$f_{(i,j,k)} ...

  2. SQL Server事务(二)

    事务的四大特性: 1.原子性:原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能 ...

  3. IDEA操作之FileHeager设置

    作用:头部注释添加,一般用于记录类的创建者等信息. 1. 打开配置 File->Settings->Editor->File and Code Templates->Inclu ...

  4. 【AtCoder】AGC006

    AGC006 A - Prefix and Suffix -- #include <bits/stdc++.h> #define fi first #define se second #d ...

  5. select key from table 一直出错

    key和keys  为mysql 关键字,数据库设计字段的时候尽量避免

  6. Python 基础(十六)--随机数模块

    random随机数模块 random.randint(1,10):随机1-10包括10 random.randrange(1,10,2):在1.3.5.7.9中随机,类似切片,不包括10 random ...

  7. Java 私有构造函数的使用

    被private修饰的私有构造函数无法在其他类中调用,也就是该类无法在其他类中实例化. 这种情况常用的使用场景:1.单例模式: 2.防止实例化. 一.单例模式 单例模式是一种常用的设计模式,思想是单例 ...

  8. winfrom_权限设置_TreeView的相关问题

    1.获取TreeView的值: 循环TreeView,获取checked每个节点的Text,串起来用逗号“,”隔开,保存到数据库. List<string> list = new List ...

  9. LeetCode 腾讯精选50题--合并K个排序链表

    今天的题目稍微有点复杂了,因为是K个有序链表的合并,看到这道题后的大体思路是这样的: 1.首先先做到两个链表的合并,链表的合并我想到的是用递归操作, 2.其次是多个链表的合并,所以在第一步实现的基础上 ...

  10. 解决设置了display:none的元素,会先展示再隐藏

    问题:元素明明设置了display:none,但是在刷新页面的时候却会先显示了出来,然后才会隐藏,实现display:none 原因:由于元素渲染的时候,样式还没有应用上去,导致的 解决办法:使用内联 ...