题解:P15649 [省选联考 2026] 找寻者 / recollector
好难啊,LA 中已经关于前后缀背包和退背包讨论很久了,但是我觉得可以利用积分来做吧。
(暂时不确定正确性,qoj 数据过了)
更新日志:
2026/3/7 23:03 修改了由 wjwWeiwei 大佬提出的关于时间复杂度的问题,并重写了代码。
设
令
设
一条边
利用积分恒等式
其中
由于
但是不难发现,这样在遇到某些度数极大的情况时,如果暴力计算前后缀的乘积,会使时间复杂度退化至
不难发现,可以直接使用多项式除法来解决。
考虑到子节点的重链长度至少为
对于每个子节点
将所有平移后的多项式
对于特定儿子
得到的商多项式对应的真实次数需要补回 shift 即可。
#include<bits/stdc++.h>
using namespace std;
const int Mod=998244353,INF=5005;
namespace Math {
long long inv[INF];
inline void prep(){
inv[1]=1;
for(int i=2;i<INF;i++)inv[i]=Mod-(Mod/i)*inv[Mod%i]%Mod;
}
inline long long qpow(long long a,long long b){
long long r=1;a%=Mod;
while(b){
if(b&1)r=r*a%Mod;
a=a*a%Mod;
b>>=1;
}
return r;
}
}
namespace Solver {
using namespace Math;
vector<int> g[INF];
vector<long long> f[INF];
long long W[INF];
int sz[INF];
void dp(int u,int p){
sz[u]=1;
vector<int> ch;
for(int v:g[u])if(v!=p){dp(v,u);sz[u]+=sz[v];ch.push_back(v);}
if(ch.empty())return f[u]={0,1},void();
int k=ch.size(),sm=0;
vector<vector<long long>> Q(k);
vector<int> mv(k);
vector<long long> TQ={1};
for(int i=0;i<k;i++){
int v=ch[i],m=1;
while(m<(int)f[v].size()&&!f[v][m])m++;
mv[i]=m;sm+=m;
Q[i].assign(f[v].size()-m,0);
for(int j=m;j<(int)f[v].size();j++)Q[i][j-m]=f[v][j];
vector<long long> nxt(TQ.size()+Q[i].size()-1,0);
for(int a=0;a<(int)TQ.size();a++)if(TQ[a])for(int b=0;b<(int)Q[i].size();b++)nxt[a+b]=(nxt[a+b]+TQ[a]*Q[i][b])%Mod;
TQ=nxt;
}
f[u].assign(sz[u]+1,0);
for(int i=0;i<k;i++){
int v=ch[i],sh=sm-mv[i],cs=TQ.size()-Q[i].size()+1;
vector<long long> C(cs,0);
long long iv=qpow(Q[i][0],Mod-2);
for(int j=0;j<cs;j++){
long long s=TQ[j];
for(int x=1;x<=j&&x<(int)Q[i].size();x++)s=(s-Q[i][x]*C[j-x]%Mod+Mod)%Mod;
C[j]=s*iv%Mod;
}
for(int a=mv[i];a<(int)f[v].size();a++){
if(f[v][a]){
long long itg=0;
for(int d=0;d<(int)C.size();d++)if(C[d])itg=(itg+C[d]*inv[a+sh+d])%Mod;
long long pb=f[v][a]*a%Mod*itg%Mod;
W[v]=(W[v]+pb)%Mod;
f[u][a+1]=(f[u][a+1]+pb)%Mod;
}
}
}
}
inline void clr(int n){
for(int i=1;i<=n;i++){
g[i].clear(),f[i].clear();
W[i]=sz[i]=0;
}
}
}
namespace yixing {
inline void sol(){
int n;cin>>n;
Solver::clr(n);
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
Solver::g[u].push_back(v),Solver::g[v].push_back(u);
}
if (n==1)return cout<<"0\n",void();
Solver::dp(1,0);
long long ans=0;
for(int i=2;i<=n;i++)ans=(ans+(Mod+1-Solver::W[i])*Solver::sz[i])%Mod;
cout<<ans<<"\n";
}
inline void Main(){
Math::prep();
int c,t;if(!(cin>>c>>t))return;
while(t--)sol();
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
yixing::Main();
return 0;
}