题目描述
给定一颗节点编号为1~n的,且以1为根的树,给出n组询问,每次询问给定一个数对(x,y),求i为x到根路径上的点(包括x和根) (y+2i)xor(y+i),对于这n组询问的答案,不需要依次输出n个数,你只需要输出他们的和对998244353的取模即可。
树的信息以及询问不会直接给出,输入数据只包含随机种子,具体生成方式请仔细阅读备注内容。
示例1
输入:3,1,1,1,1
返回值:7
备注:
输入数据包含5个整数,n,seed1,seed2,seed3,x.
树的信息生成伪代码如下
//////////////////////1/////////////////////
定义边集数组u[],v[] //u[i],v[i]表示第i条边的两个端点
定义变量seed4
定义循环变量i从1到n-1
循环体开始
seed4=(seed1+seed2)%998244353*seed3%998244353;
u[i]=i+1;
v[i]=(seed4%i)+1;
seed3=seed2;
seed2=seed1;
seed1=seed4;
循环体结束
//////////////////////1/////////////////////
询问信息生成伪代码如下,顺带一提,第一次询问的x会在输入中给出
//////////////////////2/////////////////////
定义变量lastans,初始值为0
定义变量ret,初始值为0
定义变量y,初始值为0 //含义见题干
定义函数ans(x,y),返回值为对数对(x,y)这组询问的答案 //这里“询问”的含义见题干
定义变量z
定义循环变量i从1到n
循环体开始
z=ans(x,y);
ret=(ret+z)%998244353;
lastans=z;
x=((x+lastans)^ret)%n+1;
y=lastans;
循环体结束
//////////////////////2/////////////////////
输出一个整数,表示答案。
n<=10^5,seed1,seed2,seed3<=10^9,x<=n
保证构造出的数据合法。
题目分析
题目主要给出的信息有三个:
1.根据随机给出的种子信息,会生成树的信息,主要是边集数组u[i]和v[i];
2.定义了n组询问(x, y),每组询问y都是上一组询问的结果;
3.定义了ans(x,y),对询问进行结果计算的方式,从x到根节点的特殊计算;
生成树的信息和进行n组询问都给出了具体过程代码,而对询问结果的计算需要在确定树的信息后才能进行计算,在这里做了一个特殊处理,将边集数组转化为一个子节点的父节点信息,所有的子节点最终的父节点都是根节点。
在定义边集数组中有:u[i]=i+1; v[i]=(seed4%i)+1; (u[i],v[i]表示第i条边的两个端点)
一条边的一个端点是 u[i](=i+1),则将这个端点作为一条边的是它的父节点 v[u[i]] 是 u[i]的父节点,最后转换为 parent[u[i]] = v[i](parent[i+1] = v[i]);
可以根据这个数组,从x的 parent[x]一直访问到根节点1,从而方便计算结果。
解题思路
方法1:根据边集数组,生成树结点的父子节点之间的关系,在循环中求从x到根节点的计算结果,最后将每一组询问的结果相加取模
1.定义一个长度为n+1的parent数组,来保存结点的父节点信息,根节点为1;
2.根据随机种子,生成树的信息;
3.y从0开始,进行询问,将上次询问结果作为y,进行下一次询问,同时使用一个ret记录每次询问结果的和;
4.在询问结果的计算过程中,不断从x开始向根节点计算,直到父节点是根节点;
代码实现
Java实现
long[] parent; int mod = 998244353; public long work (int n, long seed1, long seed2, long seed3, int x) { parent = new long[n+1]; // 生成树的信息,确定每个结点的父结点 for(int i=1;i<n;i++){ long seed4 = (seed1 + seed2) % mod * seed3 % mod; parent[i+1] = (seed4 % i) +1; seed3 = seed2; seed2 = seed1; seed1 = seed4; } long ret = 0; long lastans = 0; int y = 0; // 生成n组询问 for(int i=1;i<=n;i++){ // 获取每次询问的结果 long z = ans(x, y); // 将询问结果相加取模 ret = (ret + z) % mod; lastans = z; x = (int) ((x+lastans)^ret)%n + 1; y = (int) lastans; } return ret; } // 生成询问结果 long ans(int x, int y){ long a = 0; int i = x; // 从x往根节点计算 while(i != 1){ a += (y + 2*i) ^ (y+i); i = (int)parent[i]; } a += (y + 2) ^ (y + 1); return a; }
时间复杂度:,首先需要n次循环生成树的父节点信息,然后在n次的询问过程中,需要进行最多n次的循环计算到根节点,所以时间复杂度为;
空间复杂度:,需要额外n+1的空间来存储树结点的信息;
C++实现
vector<int> parent; int mod = 998244353; long long work(int n, long long seed1, long long seed2, long long seed3, int x) { parent.resize(n+1,0); // 生成树的信息,确定每个结点的父结点 for(int i=1;i<n;i++){ long long seed4 = (seed1 + seed2) % mod * seed3 % mod; parent[i+1] = (seed4 % i) +1; seed3 = seed2; seed2 = seed1; seed1 = seed4; } long long ret = 0; long long lastans = 0; int y = 0; // 生成n组询问 for(int i=1;i<=n;i++){ // 获取每次询问的结果 long long z = ans(x, y); // 将询问结果相加取模 ret = (ret + z) % mod; lastans = z; x = (int) ((x+lastans)^ret)%n + 1; y = (int) lastans; } return ret; } // 生成询问结果 long long ans(int x, int y){ long long a = 0; int i = x; // 从x往根节点计算 while(i != 1){ a += (y + 2*i) ^ (y+i); i = (int)parent[i]; } a += (y + 2) ^ (y + 1); return a; }
时间复杂度:与Java实现相同;
空间复杂度:与Java实现相同。