题解 树论

传送门

  • 看到树上贡献里面带 dis 的东西先想点分治

然而正解不是点分治……

大力反演一下,式子变成

T=1nt|Ttφ(t)μ(Tt)i=1nTφ(iT)j=1nTφ(jT)disk(iT,jT)

那么对所有是 T 的倍数的数拉出来建虚树
k=1 的部分分是所有点对的两端点权值乘距离,可以 DP
然后斯特林数拆 disk(i,j)
我赛时拆这个玩意拆错了淦
根据组合意义知是

nk=i=1k{ki}(ni)i!

发现要求所有点对的两端点权值乘 (disi,jk)
可以 O(k2siz) DP
所以最终复杂度 O(nlog2n+nk2logn)
其实如果你想我一样想不起来虚树也是可以根号分治的,过不去就是了

点击查看代码
#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;
}
posted @   Administrator-09  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示