ytq鴿鴿出的題真是毒瘤html
有一棵有\(n\)個點的樹,求有多少方案選\(k\)個聯通塊使得存在一箇中心點\(p\),全部\(k\)個聯通塊中全部點到\(p\)的距離都\(\leq L\)node
咱們先從部分分考慮:c++
\(2^{nk}\)枚舉聯通快判斷是否可行算法
指望得分:8pts數組
代碼就不給了數據結構
對於任意一個聯通塊,可能成爲它的中心點的點組成的集合也是一個聯通塊。正確性顯然測試
這裏做爲\(k\)個聯通塊中心點的點組成的集合是一個聯通塊優化
枚舉推論中中心點組成的集合,判斷其餘是否知足spa
指望得分:16ptscode
代碼也不給了
注意到樹上聯通塊中,點集爲\(S\),邊集爲\(E\),有\(|S|-|E|=1\)。正確性顯然
這樣咱們就珂以對每一個點、邊單獨考慮,經過容斥算出答案。設\(f(x)\)表示\(x\)控制的聯通塊個數,\(g(e)\)表示\(e\)兩端點都控制的聯通塊個數,易知答案爲
\[\sum_{v \in V}f(v)^k-\sum_{e \in E}g(e)^k\]
咱們考慮dp,設\(f_{i,j}\)表示\(i\)點子樹中距離\(i\)不超過\(j\)且包含\(i\)的個數,\(g_{i,j}\)表示\(i\)點子樹外距離\(i\)不超過\(j\)且包含\(i\)的個數,轉移以下:
\[f_{i,j}=\prod_{v \in son(i)}(f_{v,j-1}+1)\]
\[g_{i,j}=g_{fa_i,j-1}\prod_{v \in son(fa_i),v \neq i}(f_{v,j-2}+1)+1\]
對於每一個點\(i\),對答案的貢獻爲:
\[(f_{i,L}·g_{i,L})^k\]
對於每條邊\(e\)(設\(v\)爲深度更深的點),對答案的貢獻爲:
\[-(f_{v,L-1}·(g_{v,L}-1))^k\]
時間複雜度:\(O(nL)\)
指望得分:36pts
對於\(L=n\)的測試點,第二維的限制沒用了,去掉便可
時間複雜度:\(O(n)\)
指望得分:結合算法3可得48pts
對於鏈的狀況,珂以手推一下dp的貢獻,發現就是距離,算一下便可
時間複雜度:\(O(n)\)
指望得分:結合算法4可得52pts
這是暴力52pts的作法,代碼以下,subtask1是算法3,subtask2是算法4,subtask3是算法5
#include <bits/stdc++.h> #define mod 998244353 #define N 100005 #define getchar nc using namespace std; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int read() { register int x=0,f=1;register char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f; } inline void write(register int x) { if(!x)putchar('0');if(x<0)x=-x,putchar('-'); static int sta[20];register int tot=0; while(x)sta[tot++]=x%10,x/=10; while(tot)putchar(sta[--tot]+48); } inline int power(register int a,register int b) { int res=1; while(b) { if(b&1) res=1ll*res*a%mod; a=1ll*a*a%mod; b>>=1; } return res; } struct egde{ int to,next; }e[N<<2]; int head[N<<1],cnt=0; inline void add(register int u,register int v) { e[++cnt]=(egde){v,head[u]}; head[u]=cnt; } int n,m,k,ans; namespace subtask1{ vector<int> f[N],g[N]; inline void dfs1(register int x,register int fa) { for(register int i=head[x];i;i=e[i].next) { int v=e[i].to; if(v==fa) continue; dfs1(v,x); for(register int j=1;j<=m;++j) f[x][j]=1ll*f[x][j]*(f[v][j-1]+1)%mod; } } inline void dfs2(register int x,register int fa) { static int son[N],pre[N],suf[N]; int cnt=0; for(register int i=head[x];i;i=e[i].next) if(e[i].to!=fa) son[++cnt]=e[i].to; for(register int i=pre[0]=suf[cnt+1]=1;i<=m;++i) { if(i>=2) { for(register int j=1;j<=cnt;++j) pre[j]=1ll*pre[j-1]*(f[son[j]][i-2]+1)%mod; for(register int j=cnt;j>=1;--j) suf[j]=1ll*suf[j+1]*(f[son[j]][i-2]+1)%mod; } else { for(register int j=1;j<=cnt;++j) pre[j]=suf[j]=1; } for(register int j=1;j<=cnt;++j) g[son[j]][i]=(1ll*g[x][i-1]*pre[j-1]%mod*suf[j+1]+1)%mod; } for(register int i=head[x];i;i=e[i].next) { int v=e[i].to; if(v==fa) continue; ans=(ans+power(1ll*(g[v][m]-1)*f[v][m-1]%mod,k))%mod; dfs2(v,x); } } inline void solve() { for(register int i=1;i<=n;++i) f[i].resize(m+1,1),g[i].resize(m+1,1); dfs1(1,0); dfs2(1,0); ans=mod-ans; for(register int i=1;i<=n;++i) ans=(0ll+ans+power(1ll*f[i][m]*g[i][m]%mod,k))%mod; write(ans); } } namespace subtask2{ int a[200005],b[200005]; inline void dfs1(register int x,register int fa) { for(register int i=head[x];i;i=e[i].next) { int v=e[i].to; if(v==fa) continue; dfs1(v,x); a[x]=1ll*a[x]*(a[v]+1)%mod; } } inline void dfs2(register int x,register int fa) { static int son[200005],pre[200005],suf[200005]; int cnt=0; for(register int i=head[x];i;i=e[i].next) if(e[i].to!=fa) son[++cnt]=e[i].to; pre[0]=suf[cnt+1]=1; for(register int j=1;j<=cnt;++j) pre[j]=1ll*pre[j-1]*(a[son[j]]+1)%mod; for(register int j=cnt;j>=1;--j) suf[j]=1ll*suf[j+1]*(a[son[j]]+1)%mod; for(register int j=1;j<=cnt;++j) b[son[j]]=(1ll*b[x]*pre[j-1]%mod*suf[j+1]+1)%mod; for(register int i=head[x];i;i=e[i].next) { int v=e[i].to; if(v==fa) continue; ans=(ans+power(1ll*(b[v]-1)*a[v]%mod,k))%mod; dfs2(v,x); } } inline void solve() { for(register int i=0;i<=n;++i) a[i]=b[i]=1; dfs1(1,0); dfs2(1,0); ans=mod-ans; for(register int i=1;i<=n;++i) ans=(ans+power(1ll*a[i]*b[i]%mod,k))%mod; write(ans); } } namespace subtask3{ inline void solve() { for(register int i=1;i<=n;++i) { int l=max(1,i-m),r=min(n,i+m); ans=(ans+power(1ll*(i-l+1)*(r-i+1)%mod,k))%mod; } for(register int i=2;i<=n;++i) { int l=max(1,i-m),r=min(n,i+m-1); ans=(ans+mod-power(1ll*(i-l+1-1)*(r-i+1)%mod,k))%mod; } write(ans); } } int main() { n=read(),m=read(),k=read(); if(n<=200000) { for(register int i=1;i<n;++i) { int u=read(),v=read(); add(u,v),add(v,u); } } if(m==n&&n<=200000) subtask2::solve(); else if(n<=100000) subtask1::solve(); else subtask3::solve(); return 0; }
發現\(f,g\)的轉移珂以用長鏈剖分優化,套上一個可持久化數據結構,須要資瓷區間加、區間乘
時間複雜度:\(O(n\log n)\)
指望得分:結合算法5可得84pts
發現是全局加,後綴乘,這樣能夠像SDOI2019Day1T1 快速查詢
珂以用數組線性維護,爲了去掉求逆元的\(log\),咱們珂以用相似於求階乘逆元的方法\(O(n)\)預處理要用的逆元
至此,咱們獲得了一個\(O(n)\)的作法
指望得分:100pts
#include <bits/stdc++.h> #define mod 998244353 #define N 1000005 #define getchar nc using namespace std; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int read() { register int x=0,f=1;register char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f; } inline void write(register int x) { if(!x)putchar('0');if(x<0)x=-x,putchar('-'); static int sta[20];register int tot=0; while(x)sta[tot++]=x%10,x/=10; while(tot)putchar(sta[--tot]+48); } inline int power(register int a,register int b) { int res=1; while(b) { if(b&1) res=1ll*res*a%mod; a=1ll*a*a%mod; b>>=1; } return res; } struct edge{ int to,next; }e[N<<1]; int head[N],cnt=0; inline void add(register int u,register int v) { e[++cnt]=(edge){v,head[u]}; head[u]=cnt; } int n,m,k; int dp[N],son[N],len[N],val[N],idx[N],tot,inv[N]; int arr[N<<3],*pos=arr,*f[N<<1],*g[N],ans[2][N],Ans; inline void dfs1(register int x,register int fa) { dp[x]=1; for(register int i=head[x];i;i=e[i].next) { int v=e[i].to; if(v==fa) continue; dfs1(v,x); son[x]=len[v]>len[son[x]]?v:son[x]; dp[x]=1ll*dp[x]*dp[v]%mod; } if(++dp[x]<mod) val[idx[x]=++tot]=dp[x]; len[x]=len[son[x]]+1; } inline void init_inv() { static int pre[N]; for(register int i=pre[0]=1;i<=tot;++i) pre[i]=1ll*pre[i-1]*val[i]%mod; int pre_inv=power(pre[tot],mod-2); for(register int i=tot;i;--i) inv[i]=1ll*pre[i-1]*pre_inv%mod,pre_inv=1ll*pre_inv*val[i]%mod; } struct node{ int a,b,inv,pos,num; }; namespace F{ node tag[N<<1]; vector<pair<node,vector<pair<int,int> > > >backup[N]; inline int get(register int u,register int i) { int res=i<tag[u].pos?f[u][i]:tag[u].num; return (1ll*res*tag[u].a+tag[u].b)%mod; } inline void put(register int u,register int i,register int v) { f[u][i]=1ll*(v+mod-tag[u].b)*tag[u].inv%mod; } inline void merge(register int u,register int v,register int l) { node tmp=tag[u]; vector<pair<int,int> >vec; for(register int i=1;i<=l;++i) { vec.push_back(make_pair(i,f[u][i])); int val=get(v,i-1); if(i==tag[u].pos) f[u][tag[u].pos++]=tag[u].num; put(u,i,1ll*get(u,i)*val%mod); } if(l<m) { int val=get(v,l); if(!val) tag[u].pos=l+1,tag[u].num=mod-1ll*tag[u].b*tag[u].inv%mod; else { int t=inv[idx[v]]; vec.push_back(make_pair(0,f[u][0])); for(register int i=0;i<=l;++i) put(u,i,1ll*get(u,i)*t%mod); tag[u].a=1ll*tag[u].a*val%mod; tag[u].b=1ll*tag[u].b*val%mod; tag[u].inv=1ll*tag[u].inv*t%mod; } } if(u<=n) backup[u].push_back(make_pair(tmp,vec)); } inline void dfs2(register int x,register int fa) { if(son[x]) f[son[x]]=f[x]+1,dfs2(son[x],x),tag[x]=tag[son[x]]; else tag[x]=(node){1,1,1,n,0}; put(x,0,1); for(register int i=head[x];i;i=e[i].next) { int v=e[i].to; if(v==fa||v==son[x]) continue; f[v]=pos; pos+=len[v]; dfs2(v,x); merge(x,v,min(len[v]-1,m)); } ans[0][x]=get(x,min(len[x]-1,m-1)); ans[1][x]=get(x,min(len[x]-1,m)); ++tag[x].b; } inline void rollback(register int u) { tag[u]=backup[u].back().first; for(register int i=0;i<(backup[u].back().second).size();++i) f[u][(backup[u].back().second)[i].first]=(backup[u].back().second)[i].second; backup[u].pop_back(); } } namespace G{ node tag[N]; inline int get(register int u,register int i) { int res=i<tag[u].pos?g[u][i]:tag[u].num; return (1ll*res*tag[u].a+tag[u].b)%mod; } inline void put(register int u,register int i,register int v) { g[u][i]=1ll*(v+mod-tag[u].b)*tag[u].inv%mod; } inline void dfs3(register int x,register int fa) { if(len[x]-m-1>=0) put(x,len[x]-m-1,1); Ans=(Ans+power(1ll*ans[1][x]*get(x,len[x]-1)%mod,k))%mod; if(fa) Ans=(Ans-power(1ll*ans[0][x]*(get(x,len[x]-1)+mod-1)%mod,k)+mod)%mod; if(!son[x]) return; vector<int> ch; int maxlen=0; for(register int i=head[x];i;i=e[i].next) { int v=e[i].to; if(v==fa||v==son[x]) continue; ch.push_back(v); maxlen=max(maxlen,len[v]); } maxlen=min(maxlen,m); reverse(ch.begin(),ch.end()); f[x+n]=pos; pos+=maxlen+1; F::tag[x+n]=(node){1,1,1,n,0}; F::put(x+n,0,1); for(register int id=0;id<ch.size();++id) { int v=ch[id]; F::rollback(x); g[v]=pos; pos+=len[v]; for(register int i=max(len[v]-m-1,0);i<len[v];++i) { if(m-len[v]+i==-1) g[v][i]=get(x,len[son[x]]-len[v]+i); else g[v][i]=1ll*get(x,len[son[x]]-len[v]+i)*F::get(x,min(len[x]-1,m-len[v]+i))%mod*F::get(x+n,min(maxlen,m-len[v]+i))%mod; } tag[v]=(node){1,1,1,n,0}; F::merge(x+n,v,min(len[v]-1,m)); dfs3(v,x); } int v=son[x]; g[v]=g[x]; tag[v]=tag[x]; for(register int i=max(len[v]-m,0);i<=len[v]+maxlen-m-1;++i) { if(tag[v].pos==i) g[v][tag[v].pos++]=tag[v].num; put(v,i,1ll*get(v,i)*F::get(x+n,m-len[v]+i)%mod); } if(maxlen<m) { int vv=1,tt=1; for(register int i=0;i<ch.size();++i) vv=1ll*vv*val[idx[ch[i]]]%mod,tt=1ll*tt*inv[idx[ch[i]]]%mod; if(!vv) tag[v].pos=len[v]+maxlen-m,tag[v].num=mod-1ll*tag[v].b*tag[v].inv%mod; else { for(register int i=max(len[v]-m-1,0);i<=len[v]+maxlen-m-1;++i) put(v,i,1ll*get(v,i)*tt%mod); tag[v].a=1ll*tag[v].a*vv%mod; tag[v].b=1ll*tag[v].b*vv%mod; tag[v].inv=1ll*tag[v].inv*tt%mod; } } ++tag[v].b; dfs3(v,x); } } int main() { n=read(),m=read(),k=read(); for(register int i=1;i<n;++i) { int u=read(),v=read(); add(u,v),add(v,u); } dfs1(1,0); init_inv(); f[1]=pos; pos+=len[1]; F::dfs2(1,0); g[1]=pos; pos+=len[1]; G::tag[1]=(node){1,1,1,n,0}; G::dfs3(1,0); write(Ans); return 0; }