题目

题目大意

给你一颗带点权的树,后面有许多个询问\((u,v)\),问:

\[\sum_{i=0}^{k-1}dist(u,d_i) \ or \ a_{d_i}
\]

\(d\)为\(u\)到\(v\)路径上的点。


思考历程&正解

其实我只会我的方法……题解说得太简略了,集训队大佬Infleaking的方法完全听不懂……

首先看到这道题,就立马觉得是神仙题。

但是想到既然是树题,那应该不会太难。

于是我开始试着\(LCT\)建立联系……但是发现这个操作真是太骚了,用\(splay\)真的不好维护。

再想想线段树,但显然线段树还是不行。

到了后来,我突然想到了一个特别傻逼的做法:\(ST\)表!

这道题维护的东西和\(2\)的次幂息息相关,所以再维护的时候,左右区间必须是\(2\)的次幂。

然后想一想怎么做。

首先答案加上\(\frac{k(k+1)}{2}\),也就是\(dist\)的贡献。接着考虑加没有加上的贡献加上去。

我们可以找到满足\(2^i\leq k\)的最大\(i\),然后将区间变成两段。

我们维护左边第\(i\)位为\(1\)的个数,记作\(suml\)。

那么答案就是\(suml*2^{i}+左边的答案+右边的答案\)。

由于左边的区间是整的,所以比较好预处理。

首先对于每一位,预处理出一个前缀和,记作\(sum_{i,j}\)。

设\(f_{i,j}\)表示\(i\)到\(2^j-1\)个祖先的答案。转移是显然的,就是将其分成两个长度相等的子区间,左右区间的答案之和,加上左区间的\(i-1\)位为\(1\)的个数乘上\(2^{i-1}\)。

同样地,也求\(g_{i,j}\),定义和\(f_{i,j}\)相反。

如果路径是一条从后代到祖先的链,可以\(i\)从高到低枚举,如果\(2^i>k\)就加上区间内\(i\)位为\(1\)的个数乘上\(2^i\);否则就分成两个区间,将左区间的贡献加上之后,后面的答案就跟左区间没有关系了,那么就可以把左区间裁掉,转化成只有右区间的子问题。

现在最重要的问题是如何绕过它们的\(LCA\)。

首先从\(u\)开始跳,如果要往右边跳\(2^i\)步,判断一下是否绕过\(LCA\)。如果没有绕过,就跳过去,跳到不能跳为止。

对于后面的,可以将这样做下去的所有区间的左端点给处理出来。我们考虑倒过来求,也就是从\(v\)开始,向上跳\(lowbit(k)\)位,不要越过\(LCA\),跳到不能跳为止。用个栈将这些经过的点存起来。

于是就可以很容易地算出后面的这些区间的答案。然后我们就把后面的区间给裁掉了。

于是只剩下了那个长度为\(2\)的次幂的,跨过\(LCA\)的区间。

考虑暴力求。每次分成两段,求出两段的答案之后用和\(f\)一样的方法合并。接着我们就发现,这两段中至少有一段是被处理过的,只需要处理那段没有被处理的就行了。于是递归的过程只有\(\lg\)层。

然后就没有然后了。时间复杂度是优秀的\(O(n\lg n)\),实际上常数巨大无比……

而且代码实现不容易。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 300010
inline int input(){
char ch=getchar();
while (ch<'0' || '9'<ch)
ch=getchar();
int x=0;
do{
x=x*10+ch-'0';
ch=getchar();
}
while ('0'<=ch && ch<='9');
return x;
}
int n,a[N];
struct EDGE{
int to;
EDGE *las;
} e[N*2];
int ne;
EDGE *last[N];
int dep[N],fa[N][30];
long long sum[N][30],f[N][30],g[N][30];
inline void init(){
static int q[N];
int head=1,tail=1;
q[1]=1;
while (head<=tail){
int x=q[head++];
for (int i=0;i<29;++i)
sum[x][i]=sum[fa[x][0]][i]+(a[x]>>i&1);
dep[x]=dep[fa[x][0]]+1;
f[x][0]=g[x][0]=0;
for (int i=1;1<<i<=dep[x];++i){
fa[x][i]=fa[fa[x][i-1]][i-1];
f[x][i]=f[x][i-1]+f[fa[x][i-1]][i-1]+(sum[x][i-1]-sum[fa[x][i-1]][i-1]<<i-1);
g[x][i]=g[x][i-1]+g[fa[x][i-1]][i-1]+(sum[fa[x][i-1]][i-1]-sum[fa[x][i]][i-1]<<i-1);
}
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa[x][0]){
fa[ei->to][0]=x;
q[++tail]=ei->to;
}
}
}
inline int LCA(int u,int v){
if (dep[u]<dep[v])
swap(u,v);
for (int k=dep[u]-dep[v],i=0;k;k>>=1,++i)
if (k&1)
u=fa[u][i];
if (u==v)
return u;
for (int i=18;i>=0;--i)
if (fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
long long getsum(int u,int v,int lca,int k){
return sum[u][k]+sum[v][k]-sum[lca][k]-sum[fa[lca][0]][k];
}
long long dfs(int k,int u,int v,int lca){
if (v==lca)
return f[u][k];
if (dep[u]-dep[lca]<1<<k-1)
return dfs(k-1,u,fa[v][k-1],lca)+g[v][k-1]+(getsum(u,fa[v][k-1],lca,k-1)<<k-1);
return f[u][k-1]+dfs(k-1,fa[u][k-1],v,lca)+(sum[u][k-1]-sum[fa[u][k-1]][k-1]<<k-1);
}
int w[30],top;
int main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
n=input();
int Q=input();
for (int i=1;i<=n;++i)
a[i]=input();
for (int i=1;i<n;++i){
int u=input(),v=input();
e[ne]={v,last[u]};
last[u]=e+ne++;
e[ne]={u,last[v]};
last[v]=e+ne++;
}
init();
while (Q--){
int u=input(),v=input(),lca=LCA(u,v),k=dep[u]+dep[v]-dep[lca]*2+1,i;
long long ans=(long long)(k-1)*k>>1;
for (i=29;i>=0 && k;--i)
if (k>>i&1){
if (1<<i<=dep[u] && dep[lca]-1<=dep[fa[u][i]]){
ans+=f[u][i]+(sum[u][i]-sum[fa[u][i]][i]<<i);
u=fa[u][i];
k-=1<<i;
}
else
break;
}
else
ans+=getsum(u,v,lca,i)<<i;
top=0;
w[0]=v;
if (u==fa[lca][0])
i++;
for (int j=0;j<i;++j)
if (k>>j&1){
++top;
w[top]=fa[w[top-1]][j];
}
int x=w[top];
for (int j=i-1;j>=0;--j)
if (k>>j&1){
--top;
ans+=g[w[top]][j]+(sum[w[top]][j]-sum[fa[w[top]][j]][j]<<j);
}
else
ans+=sum[v][j]-sum[w[top]][j]<<j;
if (u!=fa[lca][0])
ans+=dfs(i,u,x,lca)+(getsum(u,x,lca,i)<<i);
printf("%lld\n",ans);
}
return 0;
}

总结

在遇到位运算和各种询问结合起来的问题时,要想到\(ST\)表……

[JZOJ6341] 【NOIP2019模拟2019.9.4】C的更多相关文章

  1. 6424. 【NOIP2019模拟2019.11.13】我的订书机之恋

    题目描述 Description Input Output Sample Input 见下载 Sample Output 见下载 Data Constraint 题解 lj题卡线段树 求出每个右端点往 ...

  2. 6392. 【NOIP2019模拟2019.10.26】僵尸

    题目描述 题解 吼题但题解怎么这么迷 考虑一种和题解不同的做法(理解) 先把僵尸离散化,h相同的钦(ying)点一个大小 (可以发现这样每种情况只会被算正好一次) 计算完全被占领的方案,然后1-方案/ ...

  3. 6389. 【NOIP2019模拟2019.10.26】小w学图论

    题目描述 题解 之前做过一次 假设图建好了,设g[i]表示i->j(i<j)的个数 那么ans=∏(n-g[i]),因为连出去的必定会构成一个完全图,颜色互不相同 从n~1染色,点i的方案 ...

  4. 6377. 【NOIP2019模拟2019.10.05】幽曲[埋骨于弘川]

    题目描述 题解 随便bb 详细题解见 https://www.cnblogs.com/coldchair/p/11624979.html https://blog.csdn.net/alan_cty/ ...

  5. 6364. 【NOIP2019模拟2019.9.20】养马

    题目描述 题解 一种显然的水法:max(0,-(点权-边权之和*2)) 这样会挂是因为在中途体力值可能会更小,所以考虑求走完每棵子树所需的至少体力值 考虑从子树往上推求出当前点的答案 设每棵子树从根往 ...

  6. 6362. 【NOIP2019模拟2019.9.18】数星星

    题目描述 题解 一种好想/好写/跑得比**记者还快的做法: 对所有询问排序,按照R递增的顺序来处理 维护每个点最后一次被覆盖的时间,显然当前右端点为R时的答案为所有时间≥L的点的权值之和 LCT随便覆 ...

  7. 6359. 【NOIP2019模拟2019.9.15】小ω的树(tree)(定期重构)

    题目描述 题解 qy的毒瘤题 CSP搞这种码农题当场手撕出题人 先按照边权从大到小建重构树,然后40%暴力修改+查找即可 100%可以定期重构+平衡规划,每次把B个询问拉出来建虚树,在虚树上暴力维护每 ...

  8. 【NOIP2019模拟2019.11.13】旅行 && GDKOI2018 还念(二分答案+dij)

    Description: 题解: 显然满足二分性. 并且每一条边要不选l要不选r. 二分的那条链肯定要选l. 考虑有两个人在走最短路,一个人一开始必须走二分的那条链,要求第一个人走的比第二个人快. 安 ...

  9. 【NOIP2019模拟2019.10.07】果实摘取 (约瑟夫环、Mobius反演、类欧、Stern-Brocot Tree)

    Description: 小 D 的家门口有一片果树林,果树上果实成熟了,小 D 想要摘下它们. 为了便于描述问题,我们假设小 D 的家在二维平面上的 (0, 0) 点,所有坐标范围的绝对值不超过 N ...

随机推荐

  1. SDL系列之 - 用画直线的方法来画正弦曲线

    线段长度无限短后就成为点,所以,现在让我们用画直线的方法来画正弦曲线吧 #include <SDL.h> #include <stdlib.h> #include <st ...

  2. tty - 显示连接标准输入的终端的文件名

    总览 (SYNOPSIS) tty [OPTION]... 描述 (DESCRIPTION) 显示 连接 标准输入 的 终端 的 文件名. -s, --silent, --quiet 什么 都 不显示 ...

  3. struts2验证器

    1. Struts2 的验证(note6,struts2-8) 1). 验证分为两种: > 声明式验证* >> 对哪个 Action 或 Model 的那个字段进行验证 >&g ...

  4. PHPExcel导出数据量过大处理

    今天使用PHPExce插件导不出数据,发现是数据量过大的原因,这里只做简单的处理. 1.导出超时处理:在执行页面添加:set_time_limit(0); 2.内存溢出:在执行页面添加:ini_set ...

  5. scala中Trait简单使用

    trait Log { def log(message:String) = println("log:" + message) } /** * 为实例混入trait * */ tr ...

  6. 引入CSS样式表(书写位置)

    CSS可以写到那个位置? 是不是一定写到html文件里面呢? 内部样式表 内嵌式是将CSS代码集中写在HTML文档的head头部标签中,并且用style标签定义,其基本语法格式如下: <head ...

  7. Spring Data之Example<>

    简单CRUD之Example动态查询 简单介绍 (部分口水话,部分来自网络,代码永远自产) 使用过Spring全家桶的各位大佬应该都知道,Spring Data这个是Spring对持久层框架的封装,比 ...

  8. NX二次开发-UFUN工程图初始化视图信息UF_DRAW_initialize_view_info

    NX9+VS2012 #include <uf.h> #include <uf_draw.h> #include <uf_obj.h> #include <u ...

  9. NX二次开发-NXOpen::WCS Class Reference

    NX11+VS2013 #include <NXOpen/Part.hxx> #include <NXOpen/PartCollection.hxx> #include < ...

  10. JS对象的可枚举属性和不可枚举属性

    昨天在写文章(转载)的时候发现了有些对象的方法是分可枚举性和不可枚举性的.简单的查了一下资料,今天来捋一捋啥是对象的可枚举啥是不可枚举. 可枚举性: 对象的每一个属性都有一个描述对象,用来描述和控制该 ...