Codeforces 1111 E. Tree(虛樹,DP)
阿新 • • 發佈:2019-03-16
cto cond using push end %d inf ces tree
題意
有一棵樹,q個詢問,每次詢問,指定一個點做樹根,再給定k個點,要求把這些點分成不超過m組的方案數,分配的限制是任意兩個有祖先關系的點不能分在同一組。題目還保證了所有的詢問的k加起來不超過1e5。
思路
如果直接在原樹上DP計數,那麽q次詢問下的DP總復雜度是平方級別的,顯然不對。
由於詢問點數的總和很少,所以考慮在虛樹上計數。(不了解虛樹的可以先學習一下,大概思想是根據詢問的點來重新建一顆包含關鍵信息,但是規模較小的樹)於是多次詢問的問題就解決了。
難點轉到考慮虛樹上的dp計數。我們按照dfs序來dp,定義f[i][j]表示遍歷到的前i個點分成j組的方案數,cnt[i]表示點i到根有多少個詢問點。
那麽對於f[i][j]:
①如果j<=cnt[i],也就說明光是把i的祖先們分開就需要用到j個組了,那麽此時點i必然無組可放,也就是說此時沒有方案行得通,於是f[i][j]=0;
②否則,f[i][j]=f[i-1][j-1]+f[i-1][j]*(j-cnt[i]).這個應該不難理解,我們是按照dfs序來dp的,dp到點i時其祖先一定已經更新完了。那麽對於新來的點i,它要麽新開一組,方案數為f[i-1][j-1],要麽加入到已有的組中,方案數為f[i-1][j]*(j-cnt[i]),(即從已有的組中扣去那些祖先所在的組)。
考慮到空間有點吃緊,可以滾動dp,當然精打細算一點,開二維數組來DP的空間也正好夠。
代碼
#include<bits/stdc++.h> #define dd(x) cout<<#x<<" = "<<x<<" " #define de(x) cout<<#x<<" = "<<x<<endl #define sz(x) int(x.size()) #define fi first #define se second #define mp make_pair #define pb push_back #define All(x) x.begin(),x.end() using namespace std; typedef long long ll; typedef pair<int,int> P; typedef priority_queue<int> BQ; typedef priority_queue<int,vector<int>,greater<int> > SQ; const int maxn=1e5+10,INF=0x3f3f3f3f,mod=1e9+7; vector<int> G[maxn],g[maxn]; int fa[maxn][32],dep[maxn],dfn[maxn],id; void dfs(int u,int f) { dep[u]=dep[f]+1; dfn[u]=++id; fa[u][0]=f; for (int i=1;i<=25;++i) fa[u][i]=fa[fa[u][i-1]][i-1]; for (auto& v:G[u]) { if (v==f) continue; dfs(v,u); } } int LCA(int x,int y) { if (dep[x]<dep[y]) swap(x,y); int d=dep[x]-dep[y]; for (int i=0;i<=25;++i) if ((d>>i)&1) x=fa[x][i]; if (x==y) return x; for (int i=25;i>=0;--i) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int s[maxn],top; int a[maxn]; bool isqry[maxn]; void build(int n) { top=0; for (int i=0;i<n;++i) { if (i-1>=0&&a[i]==a[i-1]) continue; int u=a[i]; if (!top) { s[++top]=u; continue; } int lca=LCA(u,s[top]); while (top>1&&dfn[lca]<=dfn[s[top-1]]) g[s[top-1]].pb(s[top]),g[s[top]].pb(s[top-1]),--top; if (lca!=s[top]) g[lca].pb(s[top]),g[s[top]].pb(lca),s[top]=lca; s[++top]=u; } while (top>1) g[s[top-1]].pb(s[top]),g[s[top]].pb(s[top-1]),--top; } ll f[500]; void cal(int u,int ff,int cnt,int m) { if (isqry[u]) { for (int i=m;i>=0;--i) { if (i<=cnt) f[i]=0; else f[i]=(f[i-1]+f[i]*(i-cnt)%mod)%mod; } } for (auto& v:g[u]) { if (v==ff) continue; cal(v,u,cnt+isqry[u],m); } g[u].clear(); isqry[u]=0; } int main() { int n,q; cin>>n>>q; for (int i=1;i<n;++i) { int u,v; scanf("%d%d",&u,&v); G[u].pb(v); G[v].pb(u); } dfs(1,0); while (q--) { int k,m,rt; scanf("%d%d%d",&k,&m,&rt); a[0]=rt; for (int i=1;i<=k;++i) { scanf("%d",&a[i]); isqry[a[i]]=1; } sort(a,a+k+1,[&](int x,int y){return dfn[x]<dfn[y];}); build(k+1); memset(f,0,sizeof(f)); f[0]=1; cal(rt,0,0,m); ll ans=0; for (int i=1;i<=m;++i) ans=(ans+f[i])%mod; printf("%lld\n",ans); } return 0; }
Codeforces 1111 E. Tree(虛樹,DP)