P10789 [NOI2024] 登山

rgw2010發表於2024-08-28

思路:

我們可以對於每個 \(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:
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;
}