Descriptioin

浮生有梦三千场

穷尽千里诗酒荒

徒把理想倾倒

不如早还乡

温一壶风尘的酒

独饮往事迢迢

举杯轻思量

泪如潮青丝留他方

——乌糟兽/愚青《旧词》

你已经解决了五个问题,不妨在这大树之下,吟唱旧词一首抒怀。最后的问题就是关于这棵树的,它的描述很简单。

给定一棵 \(n\) 个点的有根树,节点标号 \(1 \sim n\),11 号节点为根。

给定常数 \(k\) 。

给定 \(Q\) 个询问,每次询问给定 \(x,y\)。

求:

\(\sum\limits_{i\leq x} depth(lca(i,y))^k\)

\(lca(x,y)\) 表示节点 \(x\) 与节点 \(y\) 在有根树上的最近公共祖先。

\(depth(x)\) 表示节点 \(x\) 的深度,根节点的深度为 1。

由于答案可能很大,你只需要输出答案模 998244353 的结果。

Input

输入包含 \(n+Q\) 行。

第 1 行,三个正整数 \(n,Q,k\) 。

第 \(i=2 \sim n\) 行,每行有一个正整数 \(f_i,(1 \leq f_i \leq n)\),表示编号为 \(i\) 的节点的父亲节点的编号。

接下来 $ Q$ 行,每行两个正整数 \(x,y(1 \leq x,y \leq n)\),表示一次询问。

Output

输出包含 \(Q\) 行,每行一个整数,表示答案模 998244353 的结果。

Sample Input

5 5 2

1

4

1

2

4 3

5 4

2 5

1 2

3 2

Sample Output

15

11

5

1

6

HINT

样例解释

输入的树:

每个点的深度分别为 1,2,3,2,3。

第一个询问 \(x = 4,y = 3\),容易求出:

\(lca(1,3)=1,lca(2,3)=1,lca(3,3)=3,lca(4,3)=4\)

于是 \(depth(1) +depth(1) +depth(3) +depth(4) =1+1+9+4=15\) 。

数据范围 \(n\leq 50000,m\leq 50000,1\leq k \leq 10^9\)


想法

首先一句题外话,我真的很喜欢这种有些文学范儿的题~

言归正传!这个题跟 \([LNOI2014]LCA\) 很像,挺套路的。

离线,把所有询问按 \(x\) 从小到大排序后处理。

考虑当 \(k=1\) 时,将 \(1\leadsto [1,x]\) 每条路径上经过的点的值都加1,统计 \(1\leadsto y\) 路径上所有点的值之和就是答案

这样做的原理为 对于任意深度为 \(i\) 的点,它对答案的贡献为 $ i1-(i-1)1=1 $

那么当 \(k \neq 1\) 时也同理,任意深度为 \(i\) 的点对答案的贡献为 \(i^k-(i-1)^k=c\) ,路径上若经过这个点,则该点的值加 \(c\)

由于所有点深度固定,\(k\) 也固定, \(c\) 是很容易处理出来的。

至于如何修改与统计,那当然是树链剖分+线段树咯!(虽说 \(lct\) 也行,但想想那代码量与极大的常数还是放弃吧 )


代码

#include<cstdio>
#include<iostream>
#include<algorithm> using namespace std; int read(){
int x=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x;
} const int N = 50005;
const int xzy = 998244353;
typedef long long ll; struct node{
int v;
node *nxt;
}pool[N],*h[N];
int cnt;
void addedge(int u,int v){
node *p=&pool[++cnt];
p->v=v;p->nxt=h[u];h[u]=p;
} int fa[N],son[N],dep[N],size[N];
void dfs1(int u){
int v,sonnum=0;
size[u]=1;
for(node *p=h[u];p;p=p->nxt){
dep[v=p->v]=dep[u]+1;
dfs1(v);
if(size[v]>sonnum) sonnum=size[v],son[u]=v;
size[u]+=size[v];
}
}
int top[N],w[N],rk[N],tot;
void dfs2(int u){
int v=son[u];
if(v){
top[v]=top[u];
w[v]=++tot;
rk[tot]=v;
dfs2(v);
}
for(node *p=h[u];p;p=p->nxt)
if((v=p->v)!=son[u]){
top[v]=v;
w[v]=++tot;
rk[tot]=v;
dfs2(v);
}
} int n,m,k;
int Pow_mod(int x,int y){
int ret=1;
while(y){
if(y&1) ret=((ll)ret*x)%xzy;
x=((ll)x*x)%xzy;
y>>=1;
}
return ret;
} struct tree{
tree *ch[2];
int e,sum,lazy;
}pool2[N*2],*root;
int cnt2;
void build(tree *p,int l,int r){
p->sum=p->lazy=0;
if(l==r){
p->e=(Pow_mod(dep[rk[l]],k)-Pow_mod(dep[rk[l]]-1,k)+xzy)%xzy;
return;
}
int mid=(l+r)>>1;
build(p->ch[0]=&pool2[++cnt2],l,mid);
build(p->ch[1]=&pool2[++cnt2],mid+1,r);
p->e=((ll)p->ch[0]->e+p->ch[1]->e)%xzy;
}
void update(tree *p) { p->sum=((ll)p->ch[0]->sum+p->ch[1]->sum)%xzy; }
void pushdown(tree *p){
if(!p->lazy) return;
for(int i=0;i<2;i++){
(p->ch[i]->lazy+=p->lazy)%=xzy;
p->ch[i]->sum=((ll)p->ch[i]->sum+1ll*p->lazy*p->ch[i]->e%xzy)%xzy;
}
p->lazy=0;
}
void change(tree *p,int l,int r,int L,int R){
if(l==L && r==R){
p->lazy++;
p->sum=(p->sum+p->e)%xzy;
return;
}
pushdown(p);
int mid=(l+r)>>1;
if(R<=mid) change(p->ch[0],l,mid,L,R);
else if(L>mid) change(p->ch[1],mid+1,r,L,R);
else{
change(p->ch[0],l,mid,L,mid);
change(p->ch[1],mid+1,r,mid+1,R);
}
update(p);
}
int query(tree *p,int l,int r,int L,int R){
if(l==L && r==R) return p->sum;
pushdown(p);
int mid=(l+r)>>1;
if(R<=mid) return query(p->ch[0],l,mid,L,R);
else if(L>mid) return query(p->ch[1],mid+1,r,L,R);
return (query(p->ch[0],l,mid,L,mid)+query(p->ch[1],mid+1,r,mid+1,R))%xzy;
} void add(int x){
while(x){
change(root,1,n,w[top[x]],w[x]);
x=fa[top[x]];
}
}
int ask(int x){
int ret=0;
while(x){
(ret+=query(root,1,n,w[top[x]],w[x]))%=xzy;
x=fa[top[x]];
}
return ret;
} struct data{
int x,y,id;
bool operator < (const data &b) const{ return x<b.x; }
}d[N];
int ans[N]; int main()
{
n=read(); m=read(); k=read();
for(int i=2;i<=n;i++) {
fa[i]=read();
addedge(fa[i],i);
}
for(int i=0;i<m;i++) {
d[i].x=read(); d[i].y=read();
d[i].id=i;
}
sort(d,d+m); dep[1]=1;
dfs1(1);
top[1]=1; w[1]=++tot; rk[tot]=1;
dfs2(1);
build(root=&pool2[++cnt2],1,n); int t=0;
for(int i=0;i<m;i++){
while(t<d[i].x) add(++t);
ans[d[i].id]=ask(d[i].y);
}
for(int i=0;i<m;i++) printf("%d\n",ans[i]); return 0;
}

[bzoj5507] [洛谷P5305] [gzoi2019]旧词的更多相关文章

  1. 【BZOJ5507】[GXOI/GZOI2019]旧词(树链剖分,线段树)

    [BZOJ5507][GXOI/GZOI2019]旧词(树链剖分,线段树) 题面 BZOJ 洛谷 题解 如果\(k=1\)就是链并裸题了... 其实\(k>1\)发现还是可以用类似链并的思想,这 ...

  2. P5305 [GXOI/GZOI2019]旧词

    题目地址:P5305 [GXOI/GZOI2019]旧词 这里是官方题解 \[\sum_{i \leq x}^{}\ depth(lca(i,y))^k\] \(k = 1\) 求的是 \(\sum_ ...

  3. [LOJ3088][GXOI/GZOI2019]旧词——树链剖分+线段树

    题目链接: [GXOI/GZOI2019]旧词 对于$k=1$的情况,可以参见[LNOI2014]LCA,将询问离线然后从$1$号点开始对这个点到根的路径链修改,每次询问就是对询问点到根路径链查询即可 ...

  4. BZOJ5507 GXOI/GZOI2019旧词 (树链剖分+线段树)

    https://www.cnblogs.com/Gloid/p/9412357.html差分一下是一样的问题.感觉几年没写过树剖了. #include<iostream> #include ...

  5. luogu P5305 [GXOI/GZOI2019]旧词

    传送门 先考虑\(k=1\),一个点的深度就是到根节点的路径上的点的个数,所以\(lca(x,y)\)的深度就是\(x\)和\(y\)到根路径的交集路径上的点的个数,那么对于一个询问,我们可以对每个点 ...

  6. P5305-[GXOI/GZOI2019]旧词【树链剖分,线段树】

    正题 题目链接:https://www.luogu.com.cn/problem/P5305 题目大意 给一棵有根树和\(k\),\(Q\)次询问给出\(x,y\)求 \[\sum_{i=1}^{x} ...

  7. [GXOI/GZOI2019]旧词(树上差分+树剖)

    前置芝士:[LNOI2014]LCA 要是这题放HNOI就好了 原题:\(\sum_{l≤i≤r}dep[LCA(i,z)]\) 这题:\(\sum_{i≤r}dep[LCA(i,z)]^k\) 对于 ...

  8. [GXOI/GZOI2019]旧词

    很像LNOI 2014 LCA那道题. 同样的套路,离线以后直接扫描线. k=1的话就是原题. 考虑一般情况. 原本的做法是对x到根的这条链做一下区间+1操作,目的是为了是的在深度为i的位置得到的贡献 ...

  9. BZOJ 5507: [gzoi2019]旧词 LCT

    和之前那个 [LNOI]LCA 几乎是同一道题,就是用动态树来维护查分就行. code: #include <bits/stdc++.h> using namespace std; #de ...

随机推荐

  1. H3C VLAN基本配置

  2. C# 通过编程的方法在桌面创建回收站快捷方式

    基本所有的桌面都会存在回收站快捷方式,如果想要多创建几个快捷方式,点开就是回收站,请看本文的方法 在引用 Windows Script Host Object Model 这个 COM 方法之后可以使 ...

  3. MFC 封装类为静态链接库

    mfc自带的基本控件都不怎么美观,所以一般开发者都会自定义类对控件进行重绘.手里也积累了不少控件的重绘,对对话框.静态文本.列表框等. 但是每次都要把这些类重新导入到新的工程里,比较麻烦,而且我也不想 ...

  4. 牛客2018国庆集训 DAY1 D Love Live!(01字典树+启发式合并)

    牛客2018国庆集训 DAY1 D Love Live!(01字典树+启发式合并) 题意:给你一颗树,要求找出简单路径上最大权值为1~n每个边权对应的最大异或和 题解: 根据异或的性质我们可以得到 \ ...

  5. k8s生产环境部署

    建议配置: 服务器 900G*2 SSD 安装操作系统 CPU 16核心*2 40G网卡*2 RAM 64G*8 操作系统建议:Centos7.4/Centos 7.6 不建议使用CentOS8 分布 ...

  6. java日志学习(持续更新)

    1.Java实现日志 java日志体系大体可以分为三个部分:日志门面接口.桥接器.日志框架具体实现.原生日志实现(http://www.importnew.com/16331.html) Java日志 ...

  7. HDU1172猜数字 [模拟]

    1.题意 任务是猜一个四位数,每次尝试后会给出这次猜中了几个数字和猜中了几个位置,求能否根据尝试的记录给出答案 2.分析 数据给出查询次数和每次查询的数及其有几个数和几个位置符合,值得注意的是,猜对的 ...

  8. zabbix安装完成后查看编译参数

    最近学习zabbix分布式监控系统,突然想如何查看自己编译时的参数,最终找到自己想要的结果. 1.首先进入zabbix源码目录 2.用ls -l命令查看是否有一个叫config.log文件 3.这个文 ...

  9. ubuntu手动升级系统

    之前自己安装的是ubuntu14.04,现在需要升级到16.04,于是上网搜索了一下升级步骤以及相关命令,将这些整理出来分享给大家,希望能够给大家提供帮助. 1.更新资源: sudo apt-get ...

  10. JS-数组常用方法整理

    想了解数组有哪些原生方法,控制台输出,如图: length:数组的实例属性,返回或设置一个数组中的元素个数. toString():可以把数组转换成字符串,并返回结果. toLocaleString( ...