P10789 [NOI2024] 登山
cnblogs 2024-08-29 08:09:01 阅读 82
讲解 P10789 [NOI2024] 登山。
首先使用朴素的动态规划,前缀和优化到平方,考虑特殊性质的部分分,使用树剖进行优化,由特殊性质推到整体,使用主席树再次进行优化,中间需要多次倍增跳跃。
思路:
我们可以对于每个 \(i\) 找到它能跳到的最远的点和最近的点,倍增求一下 \(k\) 级祖先即可,令 \([l_i,r_i]\) 新表示 \(i\) 能跳到其祖先中深度在 \([l_i,r_i]\) 内的点;同时令 \(lim_i = d_i - h_i-1\) 表示 \(i\) 至少要跳到 \(lim_i\) 的深度。
考虑动态规划算法,令 \(dp_i\) 表示从 \(i\) 出发到山顶的合法登山序列个数。
那么相当于先从 \(i\) 滑落到 \(j\),然后再从 \(j\) 冲刺到 \(k\),再加上 \(dp_k\) 的方案数。
则状态转移方程为:
\[dp_i = \sum_{j 在 i 子树内} \sum_{k 为 i 的祖先} (d_k \in [l_j,\min(W(i,j),r_j)]) dp_k
\]
其中 \(W(i,j)\) 表示 \(i \to j\) 路径上 \(lim_k\) 的最小值,因为每次冲刺到达的深度必须小于所有经过的点的 \(lim_k\)。
朴素实现是 \(O(N^3)\) 的,考虑优化;注意到 \(k\) 是 \(i\) 的祖先中一段深度连续的点,则我们可以做一个深度的 \(dp_i\) 的前缀和,差分即可,时间复杂度优化至 \(O(N^2)\),可以得到 25pts。
$O(N^2)$ Code:
<code>namespace Sub1{
ll L,R,x,a,b;
ll s[N],dp[N],dep[N];
void dfs1(ll u){
for(auto v:E[u]){
if(v==fa[u])
continue;
dep[v]=dep[u]+1;
dfs1(v);
}
}
ll get(ll l,ll r){
if(!l)
return s[r];
return (s[r]-s[l-1]+mod)%mod;
}
void dfs3(ll u,ll Min,ll from){
L=l[u],R=min(r[u],Min);
if(L<=R)
dp[from]=(dp[from]+get(L,R))%mod;
for(auto v:E[u]){
if(v==fa[u])
continue;
dfs3(v,min(Min,h[v]),from);
}
}
void dfs2(ll u){
if(dep[u])
s[dep[u]]=(s[dep[u]-1]+dp[u])%mod;
else
s[dep[u]]=dp[u];
for(auto v:E[u]){
if(v==fa[u])
continue;
dfs3(v,h[v],v);
dfs2(v);
}
}
void solve(){
Read();
dfs1(1);
dp[1]=1;
For(i,2,n){
dp[i]=0;
x=i;
h[i]=dep[i]-h[i]-1;
while(r[i]){
x=fa[x];
l[i]--,r[i]--;
if(!l[i])
b=x;
if(!r[i])
a=x;
}
l[i]=dep[a],r[i]=dep[b];
}
dfs2(1);
For(i,2,n){
write(dp[i]);
putchar(' ');
}
putchar('\n');
}
void work(){
while(T--)
solve();
}
};
设 \(u^k\) 表示 \(u\) 的 \(k\) 级祖先,
现在考虑 \(l_j=r_j\) 的特殊性质,注意到对于 \(i\) 子树内的点 \(j\),它最多只会对 \(dp_i\) 造成 \(dp_{j^{l_j}}\) 的贡献,注意到当 \(W(i,j) < r_j\) 时是没有贡献的。
则我们考虑枚举 \(j\),且注意到随着 \(i\) 的深度的变小 \(W(i,j)\) 也会变小,考虑求出 \(g_j\) 表示深度最小的使得 \(W(g_j,j) \ge r_j\) 的点,直接倍增求即可。
那么对于 \(j \to g_j\) 路径上的 \(i\) 点,是可以由 \(i\) 下滑到 \(j\) 后冲刺到 \(j^{l_j}\) 的,那么对于 \(j \to g_j\) 路径上的 \(dp_i\) 的值都会增加 \(dp_{j^{l_j}}\)。
按照 \(j^{l_j}\) 的深度从小到大依次处理即可。
需要维护路径加,单点查,可以直接树剖与树状数组或线段树,时间复杂度为 \(O(N \log^2 N)\),这样我们就拿到了 45pts。
$O(N \log^2 N)$ Code:
class Tree{
public:
ll cnt=0;
ll p[N],t[N],z[N],d[N],fa[N];
struct Node{
ll l,r;
ll data;
ll tag;
}X[N<<2];
void dfs1(ll u,ll f){
p[u]=1;
for(auto v:E[u]){
if(v==f)
continue;
d[v]=d[u]+1;
fa[v]=u;
dfs1(v,u);
p[u]+=p[v];
if(p[v]>p[z[u]])
z[u]=v;
}
}
void dfs2(ll u,ll k){
dfn[u]=++cnt;
t[u]=k;
if(!z[u])
return ;
dfs2(z[u],k);
for(auto v:E[u]){
if(v==fa[u]||v==z[u])
continue;
dfs2(v,v);
}
}
void build(ll k,ll l,ll r){
X[k].l=l,X[k].r=r;
X[k].data=X[k].tag=0;
if(l==r)
return ;
ll mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void add(ll k,ll v){
X[k].data=Add(X[k].data,v);
X[k].tag=Add(X[k].tag,v);
}
void push_down(ll k){
if(X[k].tag){
add(k<<1,X[k].tag);
add(k<<1|1,X[k].tag);
X[k].tag=0;
}
}
void update(ll k,ll l,ll r,ll v){
if(X[k].l==l&&r==X[k].r){
add(k,v);
return ;
}
push_down(k);
ll mid=(X[k].l+X[k].r)>>1;
if(r<=mid)
update(k<<1,l,r,v);
else if(l>mid)
update(k<<1|1,l,r,v);
else{
update(k<<1,l,mid,v);
update(k<<1|1,mid+1,r,v);
}
}
ll query(ll k,ll i){
if(X[k].l==i&&i==X[k].r)
return X[k].data;
push_down(k);
ll mid=(X[k].l+X[k].r)>>1;
if(i<=mid)
return query(k<<1,i);
else
return query(k<<1|1,i);
}
void update(ll u,ll v,ll h){
while(t[u]!=t[v]){
if(d[t[u]]<d[t[v]])
swap(u,v);
update(1,dfn[t[u]],dfn[u],h);
u=fa[t[u]];
}
if(d[u]>d[v])
swap(u,v);
update(1,dfn[u],dfn[v],h);
}
void init(){
For(i,1,n)
z[i]=0;
cnt=0;
dfs1(1,1);
dfs2(1,1);
build(1,1,n);
}
}Tr;
namespace Sub2{
ll x,cnt;
ll g[N],dep[N],Fa[N][M],F[N][M];
vector<pi> G[N];
void dfs1(ll u){
For(j,1,M-1)
Fa[u][j]=Fa[Fa[u][j-1]][j-1];
for(auto v:E[u]){
if(v==fa[u])
continue;
dep[v]=dep[u]+1;
Fa[v][0]=u;
dfs1(v);
}
}
ll get_fa(ll u,ll k){
if(!k)
return u;
_For(j,0,M-1){
if(k>=(1ll<<j)){
k-=(1ll<<j);
u=Fa[u][j];
}
}
return u;
}
void dfs2(ll u){
For(j,1,M-1)
F[u][j]=min(F[u][j-1],F[Fa[u][j-1]][j-1]);
for(auto v:E[u]){
if(v==fa[u])
continue;
F[v][0]=h[v];
dfs2(v);
}
}
void solve(){
cnt=0;
Read();
Tr.init();
For(j,0,M-1)
Fa[1][j]=1;
dfs1(1);
For(i,0,n-1)
G[i].clear();
For(i,2,n){
x=get_fa(i,l[i]);
l[i]=r[i]=dep[x];
h[i]=dep[i]-h[i]-1;
G[l[i]].push_back({i,x});
}
For(j,0,M-1)
F[1][j]=INF;
dfs2(1);
For(i,2,n){
x=i;
_For(j,0,M-1)
if(r[i]<=F[x][j])
x=Fa[x][j];
g[i]=dep[x]+1;
if(g[i]<=dep[i])
g[i]=get_fa(i,dep[i]-g[i]);
else
g[i]=-1;
}
Tr.update(1,1,1);
For(i,0,n-1){
if(G[i].empty())
continue;
for(auto t:G[i]){
ll v=t.fi;
if(g[v]==-1)
continue;
Tr.update(v,g[v],Tr.query(1,dfn[t.se]));
}
}
For(i,2,n){
write(Tr.query(1,dfn[i]));
putchar(' ');
}
putchar('\n');
}
void work(){
while(T--)
solve();
}
};
现在考虑根据 \(l_j=r_j\) 的思路,求 \(l_j \ne r_j\) 的情况。
还是一样,枚举 \(j\),则对于一个 \(i\) 会增加 \(s_{\min(r_j,W(i,j))}-s_{l_j-1}\) 的贡献,其中 \(s_u\) 在 \(j\) 的祖先链上,表示深度为 \(1 \sim u\) 的点的 \(dp\) 值。
考虑分开计算,发现对于 \(-s_{l_j-1}\) 的部分,就是 \(l_j=r_j\) 的情况,倍增求出深度最小的点 \(i\) 使得 \(W(i,j) \ge l_j\),然后对于 \(i \to j\) 路径上的点都增加 \(-s_{l_j-1}\) 的贡献。
现在是如何处理 \(s_{\min(r_j,W(i,j))}\) 的部分,注意到肯定还是先取一段 \(r_j\),同样枚举 \(j\),倍增找到深度最小的满足 \(W(i,j) > r_j\)(注意是 \(>\)) 的 \(i\),则对于 \(j \to i\) 路径上的 \(dp\) 值,都要增加 \(s_{r_j}\)。
然后考虑处理 \(s_{W(i,j)}\) 的情况,考虑枚举 \(u\),使得 \(lim_u\) 是 \(i \to j\) 的严格后缀最小值(即最小值中深度最深的那个点),注意到我们只需要求出 \(u\) 子树内有多少个 \(j\) 满足 \(u \to j\) 的路径中没有小于等于 \(lim_u\) 的 \(lim_k\),且 \(lim_u \in [l_j,r_j]\)。
只要求出了这样的 \(j\) 的个数 \(sum\),则可以倍增求出深度最小的 \(i\) 使得 \(W(i,u) = lim_u\),那么直接对于 \(i \to u\) 的路径上的点,都会增加 \(s_{lim_u} \times sum\) 的贡献。
注意到这样操作会有先后顺序的问题,考虑再求出 \(s_x\) 时,才考虑 \(l_j-1=x\) 和 \(r_j = x\) 和 \(lim_u=x\) 的贡献。
该部分暴力 Code:
namespace Sub3{
ll Min,sum,x,a,b,c,t;
ll dep[N],dp[N],g1[N],g2[N],g3[N];
ll Fa[N][M];
vector<pi> E1[N],E2[N],E3[N];
void dfs(ll u){
For(j,1,M-1)
Fa[u][j]=Fa[Fa[u][j-1]][j-1];
for(auto v:E[u]){
if(v==fa[u])
continue;
Fa[v][0]=u;
dep[v]=dep[u]+1;
dfs(v);
}
}
ll get_fa(ll u,ll k){
if(!k)
return u;
_For(j,0,M-1){
if(k>=(1ll<<j)){
k-=(1ll<<j);
u=Fa[u][j];
}
}
return u;
}
ll get_sum(ll u){
ll ans=0;
while(u){
ans=Add(ans,dp[u]);
u=fa[u];
}
return ans;
}
void dfs2(ll u,ll k){
if(h[u]<=h[k]&&u!=k)
return ;
if(l[u]<=h[k]&&h[k]<=r[u])
sum++;
for(auto v:E[u]){
if(v==fa[u])
continue;
dfs2(v,k);
}
}
void solve(){
Read();
dfs(1);
dp[1]=1;
For(i,0,n-1){
E1[i].clear();
E2[i].clear();
E3[i].clear();
}
For(i,2,n){
dp[i]=0;
x=i;
t=h[i]+1;
h[i]=dep[i]-h[i]-1;
while(l[i]>=1||r[i]>=1||t>=1){
x=fa[x];
l[i]--,r[i]--,t--;
if(!l[i])
b=x;
if(!r[i])
a=x;
if(!t)
c=x;
}
l[i]=dep[a],r[i]=dep[b];
if(l[i])
E1[l[i]-1].push_back({i,fa[a]});
E2[r[i]].push_back({i,b});
E3[h[i]].push_back({i,c});
}
//cerr<<'\n';
For(i,2,n){
if(l[i]){
Min=INF;
x=i,a=-1;
while(fa[x]!=1){
Min=min(Min,h[x]);
if(Min<l[i])
break;
a=x;
x=fa[x];
}
g1[i]=a;
}
else
g1[i]=-1;
Min=INF;
x=i,a=-1;
while(fa[x]!=1){
Min=min(Min,h[x]);
if(Min<=r[i])
break;
a=x;
x=fa[x];
}
g2[i]=a;
x=i;
while(fa[x]!=1){
if(h[fa[x]]<h[i])
break;
x=fa[x];
}
g3[i]=x;
//cerr<<i<<':'<<g1[i]<<','<<g2[i]<<','<<g3[i]<<'\n';
}
//cerr<<'\n';
For(i,0,n-1){
for(auto t:E1[i]){
x=t.fi,a=get_sum(t.se),b=g1[x];
if(b==-1)
continue;
//cerr<<"("<<x<<"->"<<b<<")-"<<a<<'\n';
while(x!=fa[b]){
dp[x]=(dp[x]-a+mod)%mod;
x=fa[x];
}
}
for(auto t:E2[i]){
x=t.fi,a=get_sum(t.se),b=g2[x];
if(b==-1)
continue;
//cerr<<"("<<x<<"->"<<b<<")+"<<a<<'\n';
while(x!=fa[b]){
dp[x]=Add(dp[x],a);
x=fa[x];
}
}
for(auto t:E3[i]){
x=t.fi,a=get_sum(t.se),b=g3[x];
if(b==-1)
continue;
sum=0;
dfs2(x,x);
//cerr<<"("<<x<<"->"<<b<<")+"<<sum<<'*'<<a<<'\n';
while(x!=fa[b]){
dp[x]=(dp[x]+sum*a%mod)%mod;
x=fa[x];
}
}
}
For(i,2,n){
write(dp[i]);
putchar(' ');
}
putchar('\n');
}
void work(){
while(T--)
solve();
}
};
这样的话操作都是一些基本操作,链加,链查,倍增优化等,唯一需要注意的是查询 \(u\) 子树内有多少个 \(j\) 满足 \(u \to j\) 的路径中没有小于等于 \(lim_u\) 的 \(lim_k\),且 \(lim_u \in [l_j,r_j]\)。
考虑对于 \(i\) 找到 \(i \to 1\) 的路径上第一个满足 \(lim_j < lim_i\) 的 \(j\),那么令 \(Fa_i = j\)。
根据这个 \(Fa\),建出边为 \(i \to Fa_i\) 的新树,那么上述询问就变为了在求新树中 \(u\) 子树内的点 \(j\) 满足 \(lim_u \in [l_j,r_j]\) 的个数,证明显然,不再阐述。
那么可以每次将 \([l_j,r_j]\) 范围内的 \(sum_i\) 增加 \(1\),那么满足条件的数的数量就是 \(sum_{lim_u}\)。
考虑对于新树求出 dfn 序,则 \(u\) 子树内的点可以算作一段区间 \([L,R]\),然后建出前缀主席树即可快速求 \(lim_u\) 被区间包含的个数(因为要区间加,可以直接标记永久化)。
也可以使用 dsu on tree 做,但是本人太菜不会。
总时间复杂度为 \(O(N \log^2 N)\)。
注意主席树的空间要开 \(50\) 倍。
完整代码;
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
const ll N=1e5+10,M=20,mod=998244353,INF=1e9;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
int T,n;
int root[N],fa[N],l[N],r[N],h[N],id[N],_id[N],dfn[N];
vector<int> E[N],G[N];
inline void add(int u,int v){
E[u].push_back(v);
E[v].push_back(u);
}
inline void ADD(int u,int v){
G[u].push_back(v);
G[v].push_back(u);
}
inline void Read(){
n=read();
For(i,1,n){
E[i].clear();
G[i].clear();
}
For(i,2,n){
fa[i]=read(),l[i]=read(),r[i]=read(),h[i]=read();
add(fa[i],i);
}
}
class Tree{
public:
int cnt=0;
int p[N],t[N],z[N],d[N],fa[N];
struct Node{
int l,r,len;
ll data;
ll tag;
}X[N<<2];
inline void dfs1(int u,int f){
p[u]=1;
for(auto v:E[u]){
if(v==f)
continue;
d[v]=d[u]+1;
fa[v]=u;
dfs1(v,u);
p[u]+=p[v];
if(p[v]>p[z[u]])
z[u]=v;
}
}
inline void dfs2(int u,int k){
dfn[u]=++cnt;
t[u]=k;
if(!z[u])
return ;
dfs2(z[u],k);
for(auto v:E[u]){
if(v==fa[u]||v==z[u])
continue;
dfs2(v,v);
}
}
inline void build(int k,int l,int r){
X[k].len=r-l+1;
X[k].l=l,X[k].r=r;
X[k].data=X[k].tag=0;
if(l==r)
return ;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
inline void pushup(int k){
X[k].data=Add(X[k<<1].data,X[k<<1|1].data);
}
inline void add(int k,int v){
X[k].data=(X[k].data+1ll*X[k].len*v%mod)%mod;
X[k].tag=Add(X[k].tag,v);
}
inline void push_down(int k){
if(X[k].tag){
add(k<<1,X[k].tag);
add(k<<1|1,X[k].tag);
X[k].tag=0;
}
}
inline void update(int k,int l,int r,int v){
if(X[k].l==l&&r==X[k].r){
add(k,v);
return ;
}
push_down(k);
int mid=(X[k].l+X[k].r)>>1;
if(r<=mid)
update(k<<1,l,r,v);
else if(l>mid)
update(k<<1|1,l,r,v);
else{
update(k<<1,l,mid,v);
update(k<<1|1,mid+1,r,v);
}
pushup(k);
}
inline int query(int k,int l,int r){
if(X[k].l==l&&r==X[k].r)
return X[k].data;
push_down(k);
ll mid=(X[k].l+X[k].r)>>1;
if(r<=mid)
return query(k<<1,l,r);
else if(l>mid)
return query(k<<1|1,l,r);
else
return (query(k<<1,l,mid)+query(k<<1|1,mid+1,r))%mod;
}
inline int query(int k,int i){
if(X[k].l==i&&i==X[k].r)
return X[k].data;
push_down(k);
ll mid=(X[k].l+X[k].r)>>1;
if(i<=mid)
return query(k<<1,i);
else
return query(k<<1|1,i);
}
inline void update(int u,int v,int h){
while(t[u]!=t[v]){
if(d[t[u]]<d[t[v]])
swap(u,v);
update(1,dfn[t[u]],dfn[u],h);
u=fa[t[u]];
}
if(d[u]>d[v])
swap(u,v);
update(1,dfn[u],dfn[v],h);
}
inline int ask(int u,int v){
int ans=0;
while(t[u]!=t[v]){
if(d[t[u]]<d[t[v]])
swap(u,v);
ans=(ans+query(1,dfn[t[u]],dfn[u]))%mod;
u=fa[t[u]];
}
if(d[u]>d[v])
swap(u,v);
ans=(ans+query(1,dfn[u],dfn[v]))%mod;
return ans;
}
inline void init(){
For(i,1,n)
z[i]=0;
cnt=0;
dfs1(1,1);
dfs2(1,1);
build(1,1,n);
}
}Tr;
namespace Seg{
int siz[N];
int cnt,s;
struct Node{
int L,R;
int tag;
}X[N*50];
inline void dfs(int u,int fa){
id[u]=++s;
_id[s]=u;
siz[u]=1;
for(auto v:G[u]){
if(v==fa)
continue;
dfs(v,u);
siz[u]+=siz[v];
}
}
inline void tidy(){
For(i,1,cnt)
X[i]={0,0,0};
s=cnt=0;
dfs(1,1);
}
inline void build(int &k,int l,int r){
if(!k)
k=++cnt;
X[k].tag=0;
if(l==r)
return ;
int mid=(l+r)>>1;
build(X[k].L,l,mid);
build(X[k].R,mid+1,r);
}
inline void update(int &k,int l,int r,int L,int R,int v){
X[++cnt]=X[k];
k=cnt;
if(l==L&&r==R){
X[k].tag=Add(X[k].tag,v);
return ;
}
ll mid=(l+r)>>1;
if(R<=mid)
update(X[k].L,l,mid,L,R,v);
else if(L>mid)
update(X[k].R,mid+1,r,L,R,v);
else{
update(X[k].L,l,mid,L,mid,v);
update(X[k].R,mid+1,r,mid+1,R,v);
}
}
inline void Ask(int k,int l,int r,int i,int &ans){
ans=Add(ans,X[k].tag);
if(l==i&&i==r)
return ;
int mid=(l+r)>>1;
if(i<=mid)
Ask(X[k].L,l,mid,i,ans);
else
Ask(X[k].R,mid+1,r,i,ans);
}
inline int Ask(int u){
int X=0,Y=0;
Ask(root[id[u]+siz[u]-1],0,n,h[u],X);
Ask(root[id[u]-1],0,n,h[u],Y);
return (X-Y+mod)%mod;
}
};
namespace Std{
int Min,sum,x,a,b,c,t;
int dep[N],g1[N],g2[N],g3[N];
int Fa[N][M],F[N][M];
vector<pi> E1[N],E2[N],E3[N];
inline void dfs(int u){
For(j,1,M-1)
Fa[u][j]=Fa[Fa[u][j-1]][j-1];
for(auto v:E[u]){
if(v==fa[u])
continue;
Fa[v][0]=u;
dep[v]=dep[u]+1;
dfs(v);
}
}
inline int get_fa(int u,int k){
if(!k)
return u;
_For(j,0,M-1){
if(k>=(1ll<<j)){
k-=(1ll<<j);
u=Fa[u][j];
}
}
return u;
}
inline void dfs1(int u){
For(i,1,M-1)
F[u][i]=min(F[u][i-1],F[Fa[u][i-1]][i-1]);
for(auto v:E[u]){
if(v==fa[u])
continue;
F[v][0]=h[v];
dfs1(v);
}
}
inline void solve(){
Read();
For(j,0,M-1)
Fa[1][j]=1;
dfs(1);
For(i,1,n)
root[i]=0;
For(i,0,n-1){
E1[i].clear();
E2[i].clear();
E3[i].clear();
}
For(i,2,n){
a=get_fa(i,r[i]),b=get_fa(i,l[i]),c=get_fa(i,h[i]+1);
h[i]=dep[i]-h[i]-1;
l[i]=dep[a],r[i]=dep[b];
if(l[i])
E1[l[i]-1].push_back({i,fa[a]});
E2[r[i]].push_back({i,b});
E3[h[i]].push_back({i,c});
}
For(j,0,M-1)
F[1][j]=INF;
dfs1(1);
For(i,2,n){
if(l[i]){
x=i;
_For(j,0,M-1)
if(F[x][j]>=l[i])
x=Fa[x][j];
if(x==i)
g1[i]=-1;
else
g1[i]=get_fa(i,dep[i]-dep[x]-1);
}
else
g1[i]=-1;
x=i;
_For(j,0,M-1)
if(F[x][j]>r[i])
x=Fa[x][j];
if(x==i)
g2[i]=-1;
else
g2[i]=get_fa(i,dep[i]-dep[x]-1);
Min=INF,x=i;
_For(j,0,M-1)
if(F[x][j]>=h[i])
x=Fa[x][j];
ADD(i,x);
if(x==i)
g3[i]=-1;
else
g3[i]=get_fa(i,dep[i]-dep[x]-1);
}
Tr.init();
Tr.update(1,1,1);
Seg::tidy();
Seg::build(root[1],0,n);
For(i,2,n){
root[i]=root[i-1];
Seg::update(root[i],0,n,l[_id[i]],r[_id[i]],1);
a=0;
Seg::Ask(root[i],0,n,0,a);
}
For(i,0,n-1){
for(auto t:E1[i]){
x=t.fi,b=g1[x];
if(b==-1)
continue;
a=Tr.ask(1,t.se);
Tr.update(x,b,mod-a);
}
for(auto t:E2[i]){
x=t.fi,b=g2[x];
if(b==-1)
continue;
a=Tr.ask(1,t.se);
Tr.update(x,b,a);
}
for(auto t:E3[i]){
x=t.fi,b=g3[x];
if(b==-1)
continue;
a=Tr.ask(1,t.se);
sum=Seg::Ask(x);
Tr.update(x,b,1ll*sum*a%mod);
}
}
For(i,2,n){
write(Tr.query(1,dfn[i]));
putchar(' ');
}
putchar('\n');
}
inline void work(){
while(T--)
solve();
}
};
int main(){
//open("A.in","A.out");
read(),T=read();
Std::work();
return 0;
}
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。