树链剖分模板 点权 2遍dfs+线段树
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
typedef vector<int> VI;
//const ll mod=1000000007;
const int maxn=2e5+10;
//const int inf=0x3f3f3f3f;
const ll inf = 1e18;
//const int inf=0x7fffffff;
ll gcd(ll a,ll b) {
return b?gcd(b,a%b):a;
}
// freopen("1.txt","r",stdin);
// freopen("2.txt","w",stdout);
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//head
//luogu 3384 模板 轻重链剖分
//可实现操作
//
//1,将树从x到y结点最短路径上所有节点的值都加上z
//2,求树从x到y结点最短路径上所有节点的值之和
//3,将以x为根节点的子树内所有节点值都加上z
//4,求以x为根节点的子树内所有节点值之和
ll mod;
int n,m;
//预备工作,处理好边
int val[maxn];
int head[maxn],ed;//ed计数器,cnt要给dfs2用,呜呜呜
struct Edge{
int to,next;
}edge[maxn<<1];
void addedge(int u,int v)
{
edge[++ed].to=v;
edge[ed].next=head[u];
head[u]=ed;
}
//dfs1 需要做的事情
//跑出每个点的深度,每个点的父亲,
//每个非叶子的节点的子树大小(这个大小是包括自己的)
//每个非叶子的节点的重儿子
int depth[maxn];
int father[maxn],size[maxn];
int maxson[maxn],son[maxn];
void dfs1(int now,int fa,int dep)
{
//now当前节点 fa 父节点 dep 当前深度
depth[now]=dep;
father[now]=fa;
size[now]=1;//记录非叶子节点的子树大小
maxson[now]=-1;
son[now]=-1;
for(int i = head[now];i;i=edge[i].next)
{
int d=edge[i].to;
if(d==fa)
continue;
dfs1(d,now,dep+1);
size[now]+=size[d];
if(size[d]>maxson[now])
{
//标记每一个非叶子节点的重儿子
maxson[now]=size[d];
son[now]=d;
}
}
}
//dfs2
int cnt;
int id[maxn],newid[maxn];
int top[maxn];//记录当前节点所在重链的首节点
int newval[maxn];
void dfs2(int now,int first)
{
//first 当前重链的首节点
id[now]=++cnt;//记录当前节点的新编号
newval[cnt]=val[now];//按新编号记录节点,并将之前的值赋值给新的编号点
top[now]=first;//记录当前首节点
if(son[now]==-1)//是叶子节点,压根没东西
{
//自己是一条重链,可以回滚了
return ;
}
else
{
dfs2(son[now],first);//优先处理重儿子
for(int i=head[now];i;i=edge[i].next)
{
int d=edge[i].to;
if(d==son[now] || d==father[now])
continue;
//对于非叶子节点,对于每一个轻儿子,都有一个从自己开始的重链
dfs2(d,d);
}
}
}
//建树
struct Segment_Tree{
//(x,y) 二元组 表示区间加上x ,再对y取max
//也就是ai=max(ai+x,y)
//同理,区间加标记转化为 (x,-∞)
//区间覆盖就是(-∞,x)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
struct Tree{
ll sum;
ll lazy;
int l,r;
Tree()
{
sum=lazy=0;
l=r=0;
}
void init()
{
sum=lazy=0;
l=r=0;
}
}t[maxn<<2];
void push_up(int p)
{
t[p].sum=t[ls(p)].sum+t[rs(p)].sum;
t[p].sum%=mod;
t[p].l=t[ls(p)].l;
t[p].r=t[rs(p)].r;
}
void update(int p,int k)
{
t[p].sum+=k*(t[p].r-t[p].l+1);
t[p].sum%=mod;
t[p].lazy+=k;
t[p].lazy%=mod;
}
void push_down(int p)
{
// if(t[p].lazy!=0)
// cout<<p<<" "<<t[p].sum<<"\n";
if(t[p].lazy!=0)
{
update(ls(p),t[p].lazy);
update(rs(p),t[p].lazy);
t[p].lazy=0;
}
}
void build(int p,int l,int r)
{
if(l==r)
{
t[p].sum=newval[l]%mod;
t[p].l=l;
t[p].r=l;
return ;
}
int mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
ll query(int p,int l,int r,int ql,int qr)
{
if(ql<=l && r<=qr)
{
return t[p].sum;
}
push_down(p);
ll ans=0;
int mid=(l+r)>>1;
if(ql<=mid)
(ans+=query(ls(p),l,mid,ql,qr))%=mod;
if(mid<qr)
(ans+=query(rs(p),mid+1,r,ql,qr))%=mod;
return ans;
}
void change(int p,int l,int r,int ql,int qr,int k)
{
// cout<<l<<" "<<r<<" "<<ql<<" "<<qr<<" "<<k<<"\n";
if(ql<=l && r<=qr)
{
t[p].lazy+=k;
t[p].lazy%=mod;
t[p].sum+=k*(r-l+1);
t[p].sum%=mod;
return ;
}
push_down(p);
int mid=(l+r)>>1;
if(ql<=mid)
change(ls(p),l,mid,ql,qr,k);
if(mid<qr)
change(rs(p),mid+1,r,ql,qr,k);
push_up(p);
}
#undef ls
#undef rs
}S1;
ll query_sum_both(int x,int y)
{
ll ans=0;
while(top[x]!=top[y])//x,y2点不在一条链
{
if(depth[top[x]]<depth[top[y]])
swap(x,y);//默认x是那个链的深度最大的
ans+=S1.query(1,1,n,id[top[x]],id[x]);
//加上x到这条链顶部的距离
ans%=mod;//这题得取余
x=father[top[x]];
}
//x,y在一条链
if(depth[x]>depth[y])
swap(x,y);//默认x是那个dep小的
ans+=S1.query(1,1,n,id[x],id[y]);
return ans%mod;
}
ll query_sum_alone(int x)
{
// ll ans=0;
return S1.query(1,1,n,id[x],id[x]+size[x]-1)<