题解 树论
- 看到树上贡献里面带 的东西先想点分治
然而正解不是点分治……
大力反演一下,式子变成
那么对所有是 的倍数的数拉出来建虚树
的部分分是所有点对的两端点权值乘距离,可以 DP
然后斯特林数拆
我赛时拆这个玩意拆错了淦
根据组合意义知是
发现要求所有点对的两端点权值乘
可以 DP
所以最终复杂度
其实如果你想我一样想不起来虚树也是可以根号分治的,过不去就是了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
#define ull unsigned long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
bool npri[N];
ll phi[N], inv[N];
int pri[N], mu[N], pcnt;
vector<int> to[N];
const ll mod=998244353;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}
inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
namespace force{
ull ans[15];
vector<int> son[N];
short *lca[8005], *gcd[8005], dep[N];
void dfs(int u, int fa) {
lca[u][u]=u;
for (auto& v:to[u]) if (v!=fa) dep[v]=dep[u]+1, dfs(v, u);
for (auto& x:to[u]) if (x!=fa)
for (auto& y:to[u]) if (y!=x&&y!=fa)
for (auto& it1:son[x])
for (auto& it2:son[y])
lca[it1][it2]=u;
for (auto& v:to[u]) if (v!=fa)
for (auto& it:son[v])
lca[u][it]=lca[it][u]=u, son[u].pb(it);
son[u].pb(u);
}
void solve() {
for (int i=0; i<=n+1; ++i) lca[i]=new short[n+2];
for (int i=0; i<=n+1; ++i) gcd[i]=new short[n+2];
dep[1]=1; dfs(1, 0);
for (int a=0; a<=n; ++a)
for (int b=0; b<=a; ++b)
gcd[a][b]=!b?a:gcd[b][a%b]; //, assert(gcd[a][b]==::gcd(a, b));
for (int i=1; i<=n; ++i) {
for (int j=1; j<=i; ++j) {
int g=gcd[i][j];
ull tem=phi[i]*phi[j]*g/phi[g];
ull dis=dep[i]+dep[j]-2*dep[lca[i][j]];
if (i==j) {
for (int t=0; t<=k; ++t,tem=tem*dis%mod)
ans[t]=(ans[t]+tem)%mod;
}
else {
for (int t=0; t<=k; ++t,tem=tem*dis%mod)
ans[t]=(ans[t]+2*tem)%mod;
}
}
}
for (int i=0; i<=k; ++i) printf("%lld\n", (ll)(ans[i]%mod+mod)%mod);
}
}
namespace task1{
ll f[N], ans;
void solve() {
for (int t=1; t<=n; ++t)
for (int d=1; t*d<=n; ++d)
f[t*d]=(f[t*d]+t*inv[phi[t]]*mu[d])%mod;
for (int T=1; T<=n; ++T) {
ll tem=0;
for (int i=1; i<=n/T; ++i) tem=(tem+phi[i*T])%mod;
tem=tem*tem%mod;
ans=(ans+tem*f[T])%mod;
}
printf("%lld\n", (ans%mod+mod)%mod);
}
}
namespace task2{
vector<int> son[N];
pair<int, int> st[21][N<<1];
ll lis[N][325], f[N], g[N], ans;
int dep[N], back[N], id[N<<1], lg[N<<1], in[N], out[N], sqr, tot;
int lca(int a, int b) {
if (in[a]>in[b]) swap(a, b);
int t=lg[in[b]-in[a]+1]-1;
return st[t][in[a]].fir<st[t][in[b]-(1<<t)+1].fir?st[t][in[a]].sec:st[t][in[b]-(1<<t)+1].sec;
}
void dfs(int u, int fa) {
id[in[u]=++tot]=u;
for (int i=1; i<=sqr; ++i) g[i]=(g[i]+lis[u][i]*lis[u][i]%mod*dep[u])%mod;
for (auto& v:to[u]) if (v!=fa) {
dep[v]=dep[u]+1;
back[v]=u;
dfs(v, u);
for (int i=1; i<=sqr; ++i) {
g[i]=(g[i]+2*lis[u][i]*lis[v][i]%mod*dep[u])%mod;
lis[u][i]=(lis[u][i]+lis[v][i])%mod;
}
id[++tot]=u;
}
out[u]=tot;
}
void solve() {
sqr=sqrt(n);
for (int t=1; t<=n; ++t)
for (int d=1; t*d<=n; ++d)
f[t*d]=(f[t*d]+t*inv[phi[t]]*mu[d])%mod;
for (int T=1; T<=sqr; ++T)
for (int i=1; i<=n/T; ++i)
lis[i*T][T]=(lis[i*T][T]+phi[i*T])%mod;
dep[1]=1; dfs(1, 0);
for (int i=1; i<=tot; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for (int i=1; i<=tot; ++i) st[0][i]={dep[id[i]], id[i]};
int t=lg[tot]-1;
for (int i=1; i<=t; ++i)
for (int j=1; j+(1<<i)-1<=tot; ++j)
st[i][j]=st[i-1][j].fir<st[i-1][j+(1<<i-1)].fir?st[i-1][j]:st[i-1][j+(1<<i-1)];
for (int T=sqr+1; T<=n; ++T)
for (int i=1; i<=n/T; ++i)
for (int j=1; j<=n/T; ++j)
g[T]=(g[T]+phi[i*T]*phi[j*T]*dep[lca(i*T, j*T)])%mod;
for (int T=1; T<=n; ++T) {
ll t1=0, t2=0;
for (int i=1,g; i<=n/T; ++i) {
g=gcd(i, T);
t1=(t1+phi[i*T])%mod;
t2=(t2+phi[i*T]*dep[i*T])%mod;
}
ans=(ans+(2*t1*t2%mod-2*g[T])*f[T])%mod;
}
// cout<<"g: "; for (int i=1; i<=n; ++i) cout<<g[i]<<' '; cout<<endl;
printf("%lld\n", (ans%mod+mod)%mod);
}
}
namespace task{
vector<int> tem;
pair<int, int> st[21][N<<1];
int dep[N], ord[N<<1], lg[N<<1], in[N], id[N], sta[N], siz[N], tot, now, top;
ll f[N], g[N][15], fac[N], inv[N], inv2[N], ans[15], binom[15], s[15][15];
inline ll C(int n, int k) {return fac[n]*inv2[k]%mod*inv2[n-k]%mod;}
struct tree{
int id[N], tot;
vector<int> to[N];
ll f[N][11], val[N], tem[11];
void build(vector<int>& tem, int T) {
tot=0;
memset(binom, 0, sizeof(binom));
for (auto& it:tem) id[++tot]=it, val[tot]=it%T?0:phi[it];
// cout<<"id : "; for (int i=1; i<=tot; ++i) cout<<id[i]<<' '; cout<<endl;
// cout<<"val: "; for (int i=1; i<=tot; ++i) cout<<val[i]<<' '; cout<<endl;
for (int i=1; i<=tot; ++i) {
to[i].clear();
memset(f[i], 0, sizeof(f[i]));
}
}
void link(int u, int v) {to[u+1].pb(v+1);}
void dfs(int u) {
// cout<<"dfs: "<<u<<endl;
f[u][0]=val[u];
binom[0]=(binom[0]+val[u]*val[u])%mod;
for (auto& v:to[u]) {
dfs(v);
// cout<<"merge: "<<u<<' '<<v<<endl;
int dis=dep[id[v]]-dep[id[u]];
// cout<<"dis: "<<dis<<endl;
memset(tem, 0, sizeof(tem));
for (int i=0; i<=min(dis, k); ++i)
for (int j=0; i+j<=k; ++j)
tem[i+j]=(tem[i+j]+f[v][j]*C(dis, i))%mod;
// cout<<"tem: "; for (int i=0; i<=k; ++i) cout<<tem[i]<<' '; cout<<endl;
memcpy(f[v], tem, sizeof(tem));
for (int i=0; i<=k; ++i)
for (int j=0; i+j<=k; ++j)
binom[i+j]=(binom[i+j]+2*f[u][i]*f[v][j])%mod;
for (int i=0; i<=k; ++i) f[u][i]=(f[u][i]+f[v][i])%mod;
}
}
void solve() {dfs(1);}
}tr;
int lca(int a, int b) {
if (in[a]>in[b]) swap(a, b);
int t=lg[in[b]-in[a]+1]-1;
return st[t][in[a]].fir<st[t][in[b]-(1<<t)+1].fir?st[t][in[a]].sec:st[t][in[b]-(1<<t)+1].sec;
}
void dfs(int u, int fa) {
ord[in[u]=++tot]=u; siz[u]=1; id[u]=++now;
for (auto& v:to[u]) if (v!=fa) {
dep[v]=dep[u]+1;
dfs(v, u);
siz[u]+=siz[v];
ord[++tot]=u;
}
}
void solve() {
fac[0]=fac[1]=1; inv[0]=inv[1]=1; inv2[0]=inv2[1]=1; s[0][0]=1;
for (int i=2; i<=max(n, k); ++i) fac[i]=fac[i-1]*i%mod;
for (int i=2; i<=max(n, k); ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<=max(n, k); ++i) inv2[i]=inv2[i-1]*inv[i]%mod;
for (int i=1; i<=k; ++i) for (int j=1; j<=i; ++j) s[i][j]=(s[i-1][j-1]+j*s[i-1][j])%mod;
for (int t=1; t<=n; ++t)
for (int d=1; t*d<=n; ++d)
f[t*d]=(f[t*d]+t*inv[phi[t]]*mu[d])%mod;
dep[1]=1; dfs(1, 0);
for (int i=1; i<=tot; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for (int i=1; i<=tot; ++i) st[0][i]={dep[ord[i]], ord[i]};
int t=lg[tot]-1;
for (int i=1; i<=t; ++i)
for (int j=1; j+(1<<i)-1<=tot; ++j)
st[i][j]=st[i-1][j].fir<st[i-1][j+(1<<i-1)].fir?st[i-1][j]:st[i-1][j+(1<<i-1)];
for (int T=1; T<=n; ++T) {
// cout<<"T: "<<T<<endl;
tem.clear();
for (int i=1; i<=n/T; ++i) tem.pb(i*T);
sort(tem.begin(), tem.end(), [](int a, int b){return id[a]<id[b];});
for (int i=1,end=tem.size(); i<end; ++i) tem.pb(lca(tem[i-1], tem[i]));
sort(tem.begin(), tem.end(), [](int a, int b){return id[a]<id[b];});
tem.erase(unique(tem.begin(), tem.end()), tem.end());
// cout<<"tem: "; for (auto it:tem) cout<<it<<' '; cout<<endl;
tr.build(tem, T); top=0;
for (int i=0; i<tem.size(); ++i) {
while (top && id[tem[sta[top]]]+siz[tem[sta[top]]]-1<id[tem[i]]) --top;
if (top) tr.link(sta[top], i); //, cout<<"link: "<<tem[sta[top]]<<' '<<tem[i]<<endl;
sta[++top]=i;
}
tr.solve();
// cout<<"binom: "; for (int i=0; i<=k; ++i) cout<<binom[i]<<' '; cout<<endl;
for (int i=0; i<=k; ++i)
for (int j=0; j<=i; ++j)
g[T][i]=(g[T][i]+s[i][j]*binom[j]%mod*fac[j])%mod;
// cout<<"g: "; for (int i=0; i<=k; ++i) cout<<g[T][i]<<' '; cout<<endl;
for (int i=0; i<=k; ++i) ans[i]=(ans[i]+f[T]*g[T][i])%mod;
}
for (int i=0; i<=k; ++i) printf("%lld\n", ans[i]);
}
}
signed main()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n=read(); k=read();
for (int i=1,u,v; i<n; ++i) {
u=read(); v=read();
to[u].pb(v); to[v].pb(u);
}
phi[1]=mu[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<=n; ++i) {
if (!npri[i]) pri[++pcnt]=i, phi[i]=i-1, mu[i]=-1;
for (int j=1,x; j<=pcnt&&i*pri[j]<=n; ++j) {
npri[x=i*pri[j]]=1;
if (!(i%pri[j])) {phi[x]=phi[i]*pri[j]; break;}
else phi[x]=phi[i]*(pri[j]-1), mu[x]=-mu[i];
}
}
// for (int i=1; i<=100; ++i) for (int j=1; j<=100; ++j) assert(phi[i*j]==phi[i]*phi[j]*gcd(i, j)/phi[gcd(i, j)]);
// if (n<=8000) force::solve();
// else {
// task1::solve();
// if (k) task2::solve();
// }
task::solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】