【BZOJ4940】【YNOI2016】这是我自己的发明
阅读此篇文章前请先跟我大喊三声:dllxl!dllxl!dllxl!
咳咳。
题意:
Description
Input
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】这是我自己的发明的更多相关文章
- bzoj4940: [Ynoi2016]这是我自己的发明
用dfs序把询问表示成询问dfs序的两个区间中的信息 拆成至多9个询问(询问dfs序的两个前缀),对这些询问用莫队处理,时间复杂度$O(n\sqrt{m})$ #include<bits/std ...
- bzoj4940 [Ynoi2016]这是我自己的发明 莫队+dfs序
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4940 题解 对于换根操作,处理方法就很套路了. 首先先假定以 \(1\) 为根做一遍 dfs, ...
- 【bzoj4940】[Ynoi2016]这是我自己的发明 DFS序+树上倍增+莫队算法
题目描述 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. 2. 给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等, ...
- 【bzoj4940】这是我自己的发明
Portal --> bzoj4940 Solution (原题这题面到底是..怎么回事啊深深的套路qwq) 感觉自己对根号的算法还是很..没有感觉啊== 实际上这题和bzoj5016没有任何区 ...
- 洛谷P4689 [Ynoi2016]这是我自己的发明(莫队,树的dfn序,map,容斥原理)
洛谷题目传送门 具体思路看别的题解吧.这里只提两个可能对常数和代码长度有优化的处理方法. I 把一个询问拆成\(9\)个甚至\(16\)个莫队询问实在是有点珂怕. 发现询问的一边要么是一个区间,要么是 ...
- [Ynoi2016]这是我自己的发明 莫队
传送门:here 很棒的莫队题啊..... 题意: 有一棵$ n$个点的树,树上每个点有点权,有$ m$次询问: 操作1:给定两个点$ x,y$,求二元组$ (a,b)$的数量,要求$ a$在$ x$ ...
- 洛谷P4689 [Ynoi2016]这是我自己的发明 [莫队]
传送门 ynoi中比较良心不卡常的题. 思路 没有换根操作时显然可以变成dfs序莫队随便搞. 换根操作时一个子树可以变成两段区间的并集,也随便搞搞就好了. 这题完全不卡常,随便过. 代码 #inclu ...
- [Ynoi2016]这是我自己的发明(莫队)
话说这道题数据是不是都是链啊,我不手动扩栈就全 \(RE\)... 不过 \(A\) 了这题还是很爽的,通过昨晚到今天早上的奋斗,终于肝出了这题 其实楼上说的也差不多了,就是把区间拆掉然后莫队瞎搞 弱 ...
- YNOI2016 这是我自己的发明
看到这个标题立刻想到:. “绝地科学家,八倍不屏息啊,八百里外把头打啊...” 首先我们发现如果只考虑第二个操作,这棵树就是假的,我们可以直接莫队解决 如果考虑换根的话...可以把一个操作换成小于等于 ...
- Luogu4689 [Ynoi2016]这是我自己的发明 【莫队】
题目链接:洛谷 又来做Ynoi里面的水题了... 首先换根的话是一个套路,首先以1为根dfs,然后画一画就知道以rt为根,x的子树是什么了.可以拆分为2个dfs连续段. 然后如果要计算\([l_1,r ...
随机推荐
- TF基础2
1.常用API 1.图,操作和张量 tf.Graph,tf.Operation,tf.Tensor 2.可视化 TensorBoard 3.变量作用域 在TF中有两个作用域(scope),一个是nam ...
- UVALive-7198 Tall orders 微积分 二分
题目链接:https://cn.vjudge.net/problem/UVALive-7198 题意 有悬链线方程$ f(x)=a \cdot cosh(\frac{s}{a}) $, 现有两个电线杆 ...
- [USACO18FEB] Snow Boots G (离线+并查集)
题目大意:略 网上各种神仙做法,本蒟蒻只想了一个离线+并查集的做法 对所有靴子按最大能踩的深度从大到小排序,再把所有地砖按照积雪深度从大到小排序 一个小贪心思想,我们肯定是在 连续不能踩的地砖之前 的 ...
- PHP中的 Iterator 与 Generator
在讲解生成器之前先介绍一下迭代器: 在 PHP 中,通常情况下遍历数组使用 foreach 来遍历. 如果我们要想让一个对象可以遍历呢? PHP 为我们提供了 Iterator 接口,只要实现了这个接 ...
- STM32CubeMx的使用分享
1. 新建立工程(以F103ZET6为例) 2. 配置引脚(以PA0为例) 3. 配置外设(以串口为例) 4. 配置时钟 5. 外设.GPIO.中断初始化 6. 生成工程 7. 添加自己的代码 8 ...
- js数组并集,交集,差集
js数组并集,交集,差集的计算方式汇总 一. new Set 方式实现 这种方式实现起来比较简单,原理就是参考new Set可以去重的功能 ,关于去重可以点击 https://www.haorooms ...
- document.documentElement与body下clientHeight,scrollHeight等区别
本次说明仅在chrom环境下,ie等其他浏览器可能不同 1获取显示屏高度(pc和移动端,屏幕分辨率px) window.screen.height => 这个好理解,不多说. 2获取浏览器可视窗 ...
- DCL授权命令
create user 用户名//创建用户 grant DBA to 用户名//授权 revoke //撤销权限
- DQL命令(查询)
select *或字段1,字段2... from 表名 [where 条件] 提示:*符号表示取表中所有列:没有where语句表示 查询表中所有记录:有wh ...
- (转)Epoll模型详解
1. 内核中提高I/O性能的新方法epoll epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll.要使用epoll只需要这三个系统调 用:epoll_create(2), ...