牛客CSP-S提高组赛前集训营1 B 乃爱与城市拥挤程度

mac2025-12-31  8

H y p e r l i n k Hyperlink Hyperlink

https://ac.nowcoder.com/acm/contest/1100/B


D e s c r i p t i o n Description Description

给定一棵大小为 n n n的无根数,要求求出每个点距离它不大于 k k k的点流向这个点的值,以及这些点流量的乘积

数据范围: n ≤ 1 0 5 , k ≤ 10 n\leq 10^5,k\leq 10 n105,k10


S o l u t i o n Solution Solution

树形 d p dp dp,因为是无根树,所以我们先钦定一个根,这里假设为1

s i z [ i ] [ k ] siz[i][k] siz[i][k]表示以 i i i为根的子树中,距离不大于 k k k的流向这个点的值,显然可以得到方程

s i z [ i ] [ k ] = ∑ j ∈ i s o n s i z [ j ] [ k − 1 ] siz[i][k]=\sum_{j\in i_{son}} siz[j][k-1] siz[i][k]=jisonsiz[j][k1]

同样地,设 m u l [ i ] [ k ] mul[i][k] mul[i][k]表示以 i i i为根的子树中,距离不大于 k k k的流向这个点的所有点的值的乘积,得到方程

m u l [ i ] [ k ] = ∏ j ∈ i s o n ( m u l [ j ] [ k − 1 ] × s i z [ j ] [ k − 1 ] ) mul[i][k]=\prod_{j\in i_{son}}(mul[j][k-1]\times siz[j][k-1]) mul[i][k]=jison(mul[j][k1]×siz[j][k1])

注意这里我们只算了以它们为根的子树的贡献,但是实际上,在这道题中,它的祖上 k k k辈也要算入贡献当中,所以我们考虑处理这个值

s i z siz siz比较简单,我们往上跳时,计算沿途点的贡献即可

f i f_i fi表示以 i i i为扩散中心,距离不大于 k k k的流向这个点的值,则

f i = ∑ j ∈ i f a t h e r ( s i z [ j f a t h e r ] [ h ] − s i z [ j ] [ h − 1 ] ) f_i=\sum_{j\in i_{father}} (siz[j_{father}][h]-siz[j][h-1]) fi=jifather(siz[jfather][h]siz[j][h1])

h h h表示当前向上跳的高度

详细表示:

然后设 g i g_i gi表示以 i i i为扩散中心,距离不大于 k k k的流向这个点的所有点的值得乘积

则把之前的+改成*,把-改成/(乘法原理)

好像结束了?

不!!!

因为 m u l mul mul是没有算自己的(你不能提前知道自己的方案,你必须得知上面有多少)

所以,我们提前把 f f f每个阶段的值记录下来,再让 m u l mul mul乘上自己的方案即可

时间复杂度: O ( n k ) O(nk) O(nk)


C o d e Code Code

#include<cctype> #include<cstdio> #define LL long long #define N 100010 #define mod 1000000007 using namespace std;int n,l[N],k,tot,father[N]; struct node{int next,to;}e[N<<1]; inline void add(int u,int v){e[++tot]=(node){l[u],v};l[u]=tot;return;} LL f[N],g[N],siz[N][15],mul[N][15],crs[N]; inline LL read() { char c;LL d=1,f=0; while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48; while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48; return d*f; } inline LL ksm(LL x,LL y) { LL ans=1; for(;y;y>>=1,(x*=x)%=mod) if(y&1) (ans*=x)%=mod; return ans; } inline void dp(int x) { for(register int i=0;i<=k;i++) siz[x][i]=mul[x][i]=1; for(register int i=l[x];i;i=e[i].next) { int y=e[i].to; if(y==father[x]) continue; father[y]=x; dp(y); for(register int j=1;j<=k;j++) siz[x][j]+=siz[y][j-1],(mul[x][j]*=mul[y][j-1]*siz[y][j-1]%mod)%=mod; } return; } inline void dfs(int x) { int now=x;f[x]=crs[k]=siz[x][k]; for(register int i=k-1;i&&father[now];i--,now=father[now]) f[x]+=siz[father[now]][i]-siz[now][i-1],crs[i]=f[x];//记录f每个阶段的 if(father[now]) f[x]++; now=x;g[x]=mul[x][k]*f[x]%mod; for(register int i=k-1;i&&father[now];i--,now=father[now]) (g[x]*=mul[father[now]][i]*ksm(mul[now][i-1]*siz[now][i-1]%mod,mod-2)%mod)%=mod, (g[x]*=f[x]-crs[i+1])%=mod;//注意算上自己的 for(register int i=l[x];i;i=e[i].next) { int y=e[i].to; if(y==father[x]) continue; dfs(y); } return; } signed main() { // freopen("1.txt","r",stdin); // freopen("2.txt","w",stdout); n=read();k=read(); for(register int i=1,x,y;i<n;i++) { x=read();y=read(); add(x,y),add(y,x); } dp(1);dfs(1); for(register int i=1;i<=n;i++) printf("%lld ",f[i]); putchar(10); for(register int i=1;i<=n;i++) printf("%lld ",g[i]); }
最新回复(0)