Portal --> bzoj4940

Solution

  (原题这题面到底是。。怎么回事啊深深的套路qwq)

  感觉自己对根号的算法还是很。。没有感觉啊==

  实际上这题和bzoj5016没有任何区别的感觉。。。那个换根操作不过是一个幌子而已,处理出\(dfn\)序之后根据当前根\(rt\)的\(dfn\)序和\(x\)的子树范围的关系可以直接将查询变成\(dfn\)序上的至多两段区间

  然后就变成了和bzoj5016一模一样的东西。。只是区间可能有两个这样的话直接两两组合一下什么的就好了

​  具体的话还是写一下:其实我们需要处理的是这样一个式子

\[\sum\limits_{x}cnt_x(l_1,r_1)\cdot cnt_x(l_2,r_2)
\]

  然后我们来快乐处理式子

\[\begin{aligned}
&\sum\limits_{x}cnt_x(l_1,r_1)\cdot cnt_x(l_2,r_2)\\
=&\sum\limits_{x}(cnt_x(1,r_1)-cnt_x(1,l_1-1))\cdot (cnt_x(1,r_2)-cnt_x(1,l_2-1))\\
=&\sum\limits_{x}cnt_x(1,r_1)\cdot cnt_x(1,r_2)-\sum\limits_{x}cnt_x(1,r_1)\cdot cnt_x(1,l_2-1)-\sum\limits_{x}cnt_x(1,l_1-1)\cdot cnt_x(1,r_2)+\sum\limits_{x}cnt_x(1,l_1-1)\cdot cnt_x(1,l_2-1)
\end{aligned}
\]

  然后我们记\(Q(l,r)=\sum\limits_{x}cnt_x(1,l)\cdot cnt_x(1,r)\),那么也就是说我们要求的是

\[\sum\limits_{x}Q(r_1,r_2)-Q(r_1,l_2-1)-Q(l_1-1,r_2)+Q(l_1-1,l_2-1)
\]

  然后对于\(Q(l,r)\)我们可以用莫队处理

  对于每一个值(离散化之后就至多只有\(n\)个了)记一个\(cnt\)表示当前区间中的数量,具体一点的话就是假设当前的左右指针分别为\(l\)和\(r\),\(cntl[x]\)记录\([1,l]\)区间内\(x\)这个值得数量,\(cntr[x]\)记录\([1,r]\)区间内\(x\)这个值的数量,然后再用\(nowval\)记录一下当前的答案,每次移动的时候加上(或者减去)对应的贡献就好了(具体的话其实还是加上\(cntl[val[r]]\)或者\(cntr[val[l]]\),反正就是。。上面乘法中的其中一个\(-1\)了然后拆一下括号什么的就很清楚了)

  那么这样的话,对于原问题中的每组询问我们应该会得到。。至多。。\(9\)个\(Q(l,r)\)这样的询问,所以直接莫队爆搞一波就好了

  复杂度的话。。emmm虽然说算出来很大但是。。可以过嗯qwq

  

​  mark:场上真的。。实在不行就想一想根号的做法吧qwq并不是说什么题都要log的啊对不对qwq根号说不定就过了呢==

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define Pr pair<int,int>
#define mp make_pair
using namespace std;
const int N=100010,M=500010,TOP=20;
struct xxx{
int y,nxt;
}a[N*2];
int num[N];
struct Q{
int l,r,id,val;
Q(){}
Q(int l1,int r1,int id1,int val1){l=l1; r=r1; id=id1; val=val1;}
friend bool operator < (Q x,Q y){return num[x.l]==num[y.l]?x.r<y.r:num[x.l]<num[y.l];}
}q[M*10];
Pr rec1[10],rec2[10];
int f[N][TOP+1],dep[N],mxdfn[N];
int h[N],val[N],lis[N],st[N],ed[N];
ll ans[M],cntl[N],cntr[N];
int n,m,sq,cntq,dfn_t,rt,tot,cntQ;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void solve(){
sort(q+1,q+1+cntq);
int nowl=0,nowr=0;
ll nowval=0;
for (int i=1;i<=cntq;++i){
while (nowl<q[i].l)
nowval+=cntr[val[lis[++nowl]]],++cntl[val[lis[nowl]]];
while (nowr<q[i].r)
nowval+=cntl[val[lis[++nowr]]],++cntr[val[lis[nowr]]];
while (nowl>q[i].l)
nowval-=cntr[val[lis[nowl]]],--cntl[val[lis[nowl--]]];
while (nowr>q[i].r)
nowval-=cntl[val[lis[nowr]]],--cntr[val[lis[nowr--]]];
ans[q[i].id]+=nowval*q[i].val;
}
}
void prework(){
lis[0]=n;
sort(lis+1,lis+1+lis[0]);
lis[0]=unique(lis+1,lis+1+lis[0])-lis-1;
for (int i=1;i<=n;++i)
val[i]=lower_bound(lis+1,lis+1+lis[0],val[i])-lis;
}
void dfs(int fa,int x,int d){
int u;
st[x]=++dfn_t; lis[dfn_t]=x; dep[x]=d; mxdfn[x]=dfn_t;
f[x][0]=fa;
for (int i=1;i<=TOP;++i) f[x][i]=f[f[x][i-1]][i-1];
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
dfs(x,u,d+1);
mxdfn[x]=max(mxdfn[x],mxdfn[u]);
}
ed[x]=dfn_t;
}
int jump(int x,int d){
if (!d) return x;
for (int i=0;i<=TOP;++i)
if (d>>i&1)
x=f[x][i];
return x;
}
void get_seg(int x,Pr *rec,int &cnt){
int pre;
cnt=0;
if (rt==x){
rec[++cnt]=mp(1,n);
}
else if (st[rt]<st[x]||st[rt]>ed[x]){
rec[++cnt]=mp(st[x],ed[x]);
}
else{
pre=jump(rt,dep[rt]-dep[x]-1);
if (st[pre]>1) rec[++cnt]=mp(1,st[pre]-1);
if (mxdfn[pre]<n) rec[++cnt]=mp(mxdfn[pre]+1,n);
}
}
void add_q(int l1,int r1,int l2,int r2,int id){
q[++cntq]=Q(r1,r2,id,1);
if (l2-1)
q[++cntq]=Q(r1,l2-1,id,-1);
if (l1-1)
q[++cntq]=Q(l1-1,r2,id,-1);
if (l2-1&&l1-1)
q[++cntq]=Q(l1-1,l2-1,id,1);
}
void div(int x,int y,int id){
int cnt1,cnt2,l1,r1,l2,r2;
get_seg(x,rec1,cnt1);
get_seg(y,rec2,cnt2);
for (int i=1;i<=cnt1;++i)
for (int j=1;j<=cnt2;++j)
add_q(rec1[i].first,rec1[i].second,rec2[j].first,rec2[j].second,id);
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,op;
scanf("%d%d",&n,&m);
sq=sqrt(n);
for (int i=1;i<=n;++i) scanf("%d",val+i),num[i]=(i-1)/sq,lis[i]=val[i];
prework();
memset(h,-1,sizeof(h));
tot=0;
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfn_t=0; lis[0]=0;
dfs(0,1,1);
rt=1;
cntq=0; cntQ=0;
for (int i=1;i<=m;++i){
scanf("%d",&op);
if (op==1){
scanf("%d",&rt);
}
else{
scanf("%d%d",&x,&y);
div(x,y,++cntQ);
}
}
solve();
for (int i=1;i<=cntQ;++i) printf("%lld\n",ans[i]);
}

【bzoj4940】这是我自己的发明的更多相关文章

  1. bzoj4940: [Ynoi2016]这是我自己的发明

    用dfs序把询问表示成询问dfs序的两个区间中的信息 拆成至多9个询问(询问dfs序的两个前缀),对这些询问用莫队处理,时间复杂度$O(n\sqrt{m})$ #include<bits/std ...

  2. 【bzoj4940】[Ynoi2016]这是我自己的发明 DFS序+树上倍增+莫队算法

    题目描述 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. 2. 给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等, ...

  3. 【BZOJ4940】【YNOI2016】这是我自己的发明

    阅读此篇文章前请先跟我大喊三声:dllxl!dllxl!dllxl! 咳咳. 题意: Description 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. ...

  4. bzoj4940 [Ynoi2016]这是我自己的发明 莫队+dfs序

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4940 题解 对于换根操作,处理方法就很套路了. 首先先假定以 \(1\) 为根做一遍 dfs, ...

  5. [C]为什么发明指针?

    指针是C语言中广泛使用的一种数据类型. 运用指针编程是C语言最主要的风格之一.利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序 ...

  6. Bash 为何要发明 shopt 命令

    在 Bash 中,有两个内置命令用来控制 Bash 的各种可配置行为的开关(打开或关闭),这些开关称之为选项(option).其中一个命令是 set,set 命令有三种功能:显示所有的变量和函数:修改 ...

  7. 张艾迪(创始人):发明整个世界+224C个国家

    Eidyzhang:发明整个世界+224C个国家 Eidyzhang: Genius.Founder.CEO.23 I 世界级最高级创始人.世界最高级FounderCEO 出生在亚洲中国.Eidyzh ...

  8. 张艾迪(创始人):发明Global.World.224C的天才

    Eidyzhang: Genius.Founder.CEO.23 I 世界级最高级创始人.世界最高级FounderCEO 出生在亚洲中国.Eidyzhang 拥有黑色头发白色皮肤(20岁)大学辍学生. ...

  9. C++11之后,对源代码增加了UTF8和UCS4的支持(Windows内部使用Unicode,因为nt内核用的是ucs2,那是89年,utf8到了92年才发明出来)

    在C++编程中, 我们常打交道的无非是编辑器和编译器, 对编辑器起来说,我们常遇到就是乱码问题, 比如中文注释显示或是保存不了等, 解决办法就是把你的文件保存成Unicode(UTF8). 对于编译器 ...

随机推荐

  1. 如何快速解决MySQL 1032 主从错误

    3分钟解决MySQL 1032主从错误 Part1:写在最前1032错误----现在生产库中好多数据,在从库误删了,生产库更新后找不到了,现在主从不同步了,再跳过错误也没用,因为没这条,再更新还会报错 ...

  2. selenium webdriver API详解(三)

    本系列主要讲解webdriver常用的API使用方法(注意:使用前请确认环境是否安装成功,浏览器驱动是否与谷歌浏览器版本对应) 一:获取页面元素的文本内容:text 例:获取我的博客名字文本内容 代码 ...

  3. Java or Python?测试开发工程师如何选择合适的编程语言?

    很多测试开发工程师尤其是刚入行的同学对编程语言和技术栈选择问题特别关注,毕竟掌握一门编程语言要花不少时间成本,也直接关系到未来的面试和就业(不同企业/项目对技术栈要求也不一样),根据自身情况做一个相对 ...

  4. 转 gerrit

    开发环境 https://blog.csdn.net/u013207966/article/details/79112740 先记录下我的开发环境以及要正确安装gerrit需要用到的工具: Redha ...

  5. 后台程序获取JPG/GIF/PNG图片宽度、高度

    这是很久之前编写的代码,该代码是读取流数据指定位置的内容,获取图片的宽度.高度值. 由于系统更新,这些代码丢之不用,在这里存个档吧! 1. 获取gif图片宽度.高度.(binary_是图片流数据) ' ...

  6. Python登录,输入三次密码

    第一段python代码,写了一天,总算不报错了,值得纪念. 基本要求: 写一个登录界面,登录三次锁定用户 1. 包含一个用户信息文件,用户名和密码 2.黑名单文件 过程: 1.先检查是否在黑名单中,如 ...

  7. leetcode27_C++Remove Element

    给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...

  8. Numpy入门笔记第一天

    # 导入包 import numpy as np # 创建一维数组 a = np.arange(5) print "一维numpy数组", a print "数组的类型& ...

  9. PHP 整数

    实验环境php 5.2.17,参考资料:PHP手册 1.整数溢出 如果整数超出最大范围或者运算结果超出最大范围,都会转变成float <?php $a=2147483647; var_dump( ...

  10. 线段树-hdu2795 Billboard(贴海报)

    hdu2795 Billboard 题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子 思路:每次找到最大值的位子,然后减去L 线段树功能:query:区间求最大值的位子(直接 ...