P5537 【XR-3】系统设计

P5537 【XR-3】系统设计

【XR-3】系统设计

题目描述

小 X 需要你设计一个系统。

这个系统首先需要输入一棵 n 个点的有根树和一个长度为 m 的序列 a,接下来需要实现 q 个操作。

操作分两种:

  1. 1 x l r 表示设定起点为有根树的节点 x,接下来依次遍历 lr。当遍历到 i 时,从当前节点走向它的编号第 ai 小的儿子。如果某一时刻当前节点的儿子个数小于 ai,或者已经遍历完 lr,则在这个点停住,并输出这个点的编号,同时停止遍历。
  2. 2 t k 表示将序列中第 t 个数 at 修改为 k

输入格式

第一行 3 个正整数 n,m,q,分别表示树的点数、序列的长度和操作个数。

第二行 n 个整数 f1n,其中 fi 表示点 i 在树中的父亲节点编号,特别地,设根节点为 rt,则 frt=0

第三行 m 个正整数 a1m,表示序列 a

接下来 q 行,每行描述一个操作。

数据范围:

  • 1n,m,q5×105
  • 1ain
  • 对于操作 1,保证 1xn1lrm
  • 对于操作 2,保证 1tm1kn

输出格式

对于每个操作 1,一行一个正整数,表示答案。

用hash[x]来维护从rt走到x的前缀。
(维护的是一个表示路径的数字序列,表示从rt走到x每次走的是编号第几大的儿子)

形式化的:一段路径{1,5,8,3}表示从rt到x这条路径是访问了rt的第1大儿子(记为x1),x1的第5大儿子x2.....

这样,我们处理每次询问时,我们认为[l,r]构成一个序列。
只需要二分出最大的ans,使得从x开始的访问序列[l,ans]合法

将从rt到某个点mid的路径 rt->mid 转化为从x到mid的路径 __x->mid__的方法:

由于我们存的是路径hash,只需要在询问时差分就好了。

那么现在我们面临的问题就只有储存一段区间上路径的hash了:
由于我不想写线段树
由于我想复习一下树状数组(确信)
所以我很自然的想到了树状数组

code:

#include<bits/stdc++.h>
const int N=5e5+5;
const int P=131313131;
using namespace std;
typedef unsigned long long ull;
int n,m,rt,q;
int dep[N],fa[N],a[N];
ull hsh[N],p[N],t[N],p2[N];
unordered_map<ull,int> Map;
vector<int> G[N];
void dfs(int u,int f)
{
int tot=0;
dep[u]=dep[f]+1;
for(int v:G[u])
{
tot++;
hsh[v]=hsh[u]*P+tot;
Map[hsh[v]]=v;
dfs(v,u);
}
}
int lb(int x)
{
return -x&x;
}
void add(int x,ull y)
{
for(int i=x;i<=n;i+=lb(i))
{
t[i]+=y;
y*=p[lb(i)];
}
}
ull query_pos(int x)
{
ull y=1,res=0;
for(int i=x;i;i-=lb(i))
{
res+=t[i]*y;
y*=p[lb(i)];
}
return res;
}
ull query_range(int l,int r)
{
return query_pos(r)-query_pos(l-1)*p[r-l+1];
}
bool check(int x,int L,int mid)
{
ull ask=hsh[x]*p[mid-L+1]+query_range(L,mid);
return Map.find(ask)!=Map.end();
}
void work()
{
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
{
scanf("%d",&fa[i]);
if(!fa[i])rt=i;
else
{
G[fa[i]].push_back(i);
}
}
p[0]=1;
for(int i=1;i<=m;i++)
{
p[i]=p[i-1]*P;
}
for(int i=1;i<=n;i++)
{
sort(G[i].begin(),G[i].end());
}
dfs(rt,0);
for(int i=1;i<=m;i++)
{
scanf("%d",&a[i]);
add(i,a[i]);
}
for(int i=1,opt,l,r,x;i<=q;i++)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d%d",&x,&l,&r);
int L=l;l--;
while(l<r)
{
int mid=l+r+1>>1;
check(x,L,mid)? l=mid : r=mid-1;
}
ull val=hsh[x]*p[l-L+1]+query_range(L,l);
int ans=Map[val];
printf("%d\n",ans? ans:x);
}
else
{
scanf("%d%d",&l,&x);
add(l,x-a[l]);
a[l]=x;
}
}
}
int main()
{
freopen("P5537.in","r",stdin);freopen("P5537.out","w",stdout);
work();
}
posted @   liuboom  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示