[Ynoi2016]这是我自己的发明 莫队
传送门: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]这是我自己的发明 莫队的更多相关文章
- 洛谷P4689 [Ynoi2016]这是我自己的发明 [莫队]
传送门 ynoi中比较良心不卡常的题. 思路 没有换根操作时显然可以变成dfs序莫队随便搞. 换根操作时一个子树可以变成两段区间的并集,也随便搞搞就好了. 这题完全不卡常,随便过. 代码 #inclu ...
- bzoj4940 [Ynoi2016]这是我自己的发明 莫队+dfs序
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4940 题解 对于换根操作,处理方法就很套路了. 首先先假定以 \(1\) 为根做一遍 dfs, ...
- 【洛谷 P4688】 [Ynoi2016]掉进兔子洞(bitset,莫队)
题目链接 第一道Ynoi 显然每次询问的答案为三个区间的长度和减去公共数字个数*3. 如果是公共数字种数的话就能用莫队+bitset存每个区间的状态,然后3个区间按位与就行了. 但现在是个数,bits ...
- 洛谷P4689 [Ynoi2016]这是我自己的发明(莫队,树的dfn序,map,容斥原理)
洛谷题目传送门 具体思路看别的题解吧.这里只提两个可能对常数和代码长度有优化的处理方法. I 把一个询问拆成\(9\)个甚至\(16\)个莫队询问实在是有点珂怕. 发现询问的一边要么是一个区间,要么是 ...
- [Ynoi2016]这是我自己的发明(莫队)
话说这道题数据是不是都是链啊,我不手动扩栈就全 \(RE\)... 不过 \(A\) 了这题还是很爽的,通过昨晚到今天早上的奋斗,终于肝出了这题 其实楼上说的也差不多了,就是把区间拆掉然后莫队瞎搞 弱 ...
- 【bzoj4940】[Ynoi2016]这是我自己的发明 DFS序+树上倍增+莫队算法
题目描述 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. 2. 给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等, ...
- Luogu4689 [Ynoi2016]这是我自己的发明 【莫队】
题目链接:洛谷 又来做Ynoi里面的水题了... 首先换根的话是一个套路,首先以1为根dfs,然后画一画就知道以rt为根,x的子树是什么了.可以拆分为2个dfs连续段. 然后如果要计算\([l_1,r ...
- 洛谷P4689 [Ynoi2016]这是我自己的发明(树上莫队+树链剖分)
题目描述 您正在打galgame,然后突然家长进来了,于是您假装在写数据结构题: 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1.将树根换为 x. 2.给出两个点 x,y,从 ...
- luogu P4688 [Ynoi2016]掉进兔子洞 bitset 莫队
题目链接 luogu P4688 [Ynoi2016]掉进兔子洞 题解 莫队维护bitset区间交个数 代码 // luogu-judger-enable-o2 #include<cmath&g ...
随机推荐
- python 装饰器的应用
import time def test1(): print "hello\n" print test1.__name__ def test2(): print "hel ...
- JS怎么判断一个对象是否为空
昨天面试的时候被问到的问题.只怪自己根基不牢,没有回答好 甚至说出了“判断这个obj是否和{}相等”这样鱼蠢的答案(/(ㄒoㄒ)/~~)引用类型怎么可以直接判断==或者===呢?! 今天中秋佳节,宝宝 ...
- POJ 1815 Friendship (Dinic)
Friendship Time Limit: 2000MS Memory Limit: 20000K Total Submissions: 11429 Accepted: 3173 Descr ...
- java linux sdk1.8
wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-co ...
- java中Comparatable接口和Comparator接口的区别
1.不同类型的排序规则 .自然排序是什么? 自然排序是一种升序排序.对于不同的数据类型,升序规则不一样: BigDecimal BigInteger Byte Double Float Int ...
- (DFS)P1605 迷宫 洛谷
题目背景 迷宫 [问题描述] 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和 终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫 中移动有上下 ...
- (Bash博弈 大数) 51nod1068 Bash游戏 V3
1068 Bash游戏 V3 有一堆石子共有N个.A B两个人轮流拿,A先拿.每次拿的数量只能是2的正整数次幂,比如(1,2,4,8,16....),拿到最后1颗石子的人获胜.假设A B都非常聪明 ...
- 根据指定的key,将二维数组的value转换为string,适用于mysql的in查询
function array_unique_join($arr,$param){ $utm_source_arr = array_unique(array_column($arr,$param)); ...
- 写给IT技术爱好者的一封信
写给IT技术爱好者的一封信>当前运维素质的分析<... ---------------------- 虽相貌平平,但勤学苦练,亦收获颇丰!如果你决定要成为一名IT从业者,你需要承受以下的东 ...
- 如何在springcloud分布式系统中实现分布式锁?
一.简介 一般来说,对数据进行加锁时,程序先通过acquire获取锁来对数据进行排他访问,然后对数据进行一些列的操作,最后需要释放锁.Redis 本身用 watch命令进行了加锁,这个锁是乐观锁.使用 ...