题意简述
给你一颗n<=1e5个节点树,1e5次询问,每次给你(l,r,z),求[l,r]区间内每个数和z在树上LCA的深度的和。对201314取膜。
注:根节点深度是1。
思路框架
(x,y)的LCA深度就是,把x到根点权+1,然后询问根到y点权和多少。
那么我们相当于,把[l,r]每个点到根点权都+1,然后询问根到z的点权和。差分做。
具体思路
差分做法:把一个询问(l,r,z)拆成(1,r,z)和(1,l-1,z)。(l,r,z)的答案显然就是(1,r,z)-(1,l-1,z)。这样我们要求的就是若干的前缀点的答案了。
我们把l-1和r都打上标记i从1到n遍历一下,不断在根到i的路径上点权+1。的如果某个点有标记,那么就把所有的z拿出来询问一遍,更新询问的答案。这样是O(nlogn+q)。
还有,对于每个标记,还要记录是l-1还是r,因为l-1的答案还要带一个负号。
代码
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 54444
#define mod 201314
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
class Graph //这个是图。跳到60行
//如果你有个IDE,那么直接折叠起来好了
{
public:
int head[N];
int EdgeCount;
struct Edge
{
int To,Label,Next;
}Ed[N<<3];
void clear()
{
memset(head,-1,sizeof(head));
memset(Ed,-1,sizeof(Ed));
EdgeCount=0;
}
void AddEdge(int u,int v,int w=1)
{
++EdgeCount;
Ed[EdgeCount]=(Edge){
v,w,head[u]};
head[u]=EdgeCount;
}
void Add2(int u,int v,int w=1)
{
AddEdge(u,v,w);AddEdge(v,u,w);
}
int Start(int u)
{
return head[u];
}
int To(int u)
{
return Ed[u].To;
}
int Label(int u)
{
return Ed[u].Label;
}
int Next(int u)
{
return Ed[u].Next;
}
}G;
class SegmentTree //这个是线段树。支持区间加Add(l,r,x),还有区间求和Query(l,r)。
//如果您会,跳到138行.
{
public:
struct node
{
int l,r;
int s,a;
}tree[N<<2];
#define ls index<<1
#define rs index<<1|1
#define L tree[index].l