XJOI 旅行(树形DP)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XJOI 旅行(树形DP)相关的知识,希望对你有一定的参考价值。
题意非常清真,就是问你一棵无根树的所有可能的中dfs序中,有多少个字典序严格小于给定的一个排列.
但是也非常难写.
先来分类讨论一波:
第一部分:
如果树根小于排列的第一个数,那么所有可能的dfs序都会加到答案中去
那么统计一下每个节点的度数,
设f(x)表示以x为根节点的dfs序种类数,
f(x)=fac[du[x]]*fac[du[1]-1]*fac[du[2]-1].......(注意,后面不乘fac[du[x]-1])
其中du[x]表示x节点的度,也就是连的无向边的数量
fac[x]表示x!
先处理出f[1]
int now=fac[du[1]]; for (int i=2; i<=n; i++) now=1ll*now*fac[du[i]-1]%M;
然后,就可以O(n)的求出所有f值,加到ans上
for (int i=1; i<bb[1]; i++){
ans=(ans+now)%M;
now=1ll*now*fp(du[i],M-2)%M*du[i+1]%M;
}
fp是快速幂的意思,M是取模的数,fp(du[i],M-2)就是求du[i]的逆元.
然后第一部分就完成了
第二部分:
如果根节点是排列的第一个数,
那么就分两部分统计答案,
一是现在的点的儿子中有多少个没有遍历过又比下一个排列小的
二是现在的点的儿子如果有一个就是下一个排列,就统计子树中的方案数乘以除了这棵子树之外任意乱走的方案数.
具体实现细节很多非常麻烦,例如一棵子树如果没有走完就跳出就不再统计答案.
然后为了防止被卡菊花图还要写一个数据结构.
这是不带数据结构的:
#include<cstdio> #include<vector> #include<algorithm> using namespace std; const int M=1e9+7,N=3e5+10; int fi[N],ne[N*2],b[N*2],bb[N],visit[N],tfa[N]; int k,n,ans,u,v,ind; bool flag=1; void add(int x,int y){ b[++k]=y; ne[k]=fi[x]; fi[x]=k; } inline int fp(int x,int y){ int res=1; for (; y; y>>=1,x=1ll*x*x%M) if (y&1) res=1ll*res*x%M; return res; } int fac[N]; inline void prew(){ fac[0]=1; for (int i=1; i<=n; i++) fac[i]=1ll*fac[i-1]*i%M;//if factorial 0=1 } int re[N],son[N]; int calc(int x,int fa){ re[x]=1ll*re[x]*(son[x]+1)%M*re[fa]%M*fp(re[x],M-2)%M*fp(son[fa],M-2)%M; son[x]++; return re[x]; } void first(int x,int fa,const int ind){ re[x]=1; son[x]=0; for (int j=fi[x]; j; j=ne[j]) if (b[j]!=fa){ first(b[j],x,ind); son[x]++; re[x]=1ll*re[x]*re[b[j]]%M; if (ind) tfa[b[j]]=x; } re[x]=1ll*re[x]*fac[son[x]]%M; } void dfs(int x,int fa){ for (int j=fi[x]; j; j=ne[j]) if (b[j]!=fa){ int re0=re[x],son0=son[x];//original re[x] if (b[j]<bb[1]) ans=(ans+calc(b[j],x))%M; else calc(b[j],x); dfs(b[j],x); re[x]=re0; son[x]=son0; } } int change(int x,int fa){ visit[x]=1; ++ind; --son[fa]; int res=0,t=1ll*re[x]*fp(son[x],M-2)%M; while (1){ int numlittle=0; for (int j=fi[x]; j; j=ne[j]) if (b[j]<bb[ind+1]&&!visit[b[j]]) ++numlittle; res=(res+1ll*numlittle*t%M)%M; if (tfa[bb[ind+1]]==x){ t=1ll*t*fp(re[bb[ind+1]],M-2)%M; res=(res+1ll*t*change(bb[ind+1],x)%M)%M; if (!flag) return res; t=1ll*t*fp(son[x],M-2)%M; } else{ if (son[x]) flag=0; break; } } return res; } int main(){ scanf("%d",&n); prew(); for (int i=1; i<=n; i++) scanf("%d",&bb[i]); for (int i=1; i<n; i++){ scanf("%d%d",&u,&v); add(u,v); add(v,u); } first(1,0,0); if (1<bb[1]) ans=re[1]; dfs(1,0); first(bb[1],0,1); ans=(ans+change(bb[1],0))%M; printf("%d\\n",ans); }
这是加了zkw的:
#include<cstdio> #include<vector> #include<algorithm> using namespace std; const int M=1e9+7,N=3e5+10; int fi[N],ne[N*2],b[N*2],bb[N],visit[N],tfa[N],op[N],du[N]; int k,n,ans,u,v,ind; bool flag=1; vector<int>g[N]; namespace zkw{ vector<int>st[N]; int u[N]; inline void update(int x,int y,int z){ for (int i=u[x]+y; i; i>>=1) st[x][i]+=z; } int ask(int x,int s,int t){ if (t>=st[x].size()) t=st[x].size()-1; int res=0; for (s+=u[x]-1,t+=u[x]+1; s^t^1; s>>=1,t>>=1){ if (~s&1) res+=st[x][s^1]; if (t&1) res+=st[x][t^1]; } return res; } void prew(int x){ for (u[x]=1; u[x]<g[x].size(); u[x]<<=1); u[x]--; for (int i=1; i<=u[x]+g[x].size()+1; i++) st[x].push_back(0); for (vector<int>::iterator it=g[x].begin(); it!=g[x].end(); it++) update(x,op[*it],1); } } void add(int x,int y){ b[++k]=y; ne[k]=fi[x]; fi[x]=k; } inline int fp(int x,int y){ int res=1; for (; y; y>>=1,x=1ll*x*x%M) if (y&1) res=1ll*res*x%M; return res; } int fac[N]; inline void prew(){ fac[0]=1; for (int i=1; i<=n; i++) fac[i]=1ll*fac[i-1]*i%M;//if factorial 0=1 } int re[N],son[N]; void first(int x,int fa){ re[x]=1; for (int j=fi[x]; j; j=ne[j]) if (b[j]!=fa){ first(b[j],x); son[x]++; re[x]=1ll*re[x]*re[b[j]]%M; g[x].push_back(b[j]); tfa[b[j]]=x; } re[x]=1ll*re[x]*fac[son[x]]%M; } int change(int x,int fa){ if (fa) zkw::update(fa,op[x],-1); ++ind; --son[fa]; int res=0,t=1ll*re[x]*fp(son[x],M-2)%M; while (1){ int numlittle=0; if (!g[x].empty()){ vector<int>::iterator it=lower_bound(g[x].begin(),g[x].end(),bb[ind+1]); numlittle=zkw::ask(x,1,it-g[x].begin()); } res=(res+1ll*numlittle*t%M)%M; if (tfa[bb[ind+1]]==x){ t=1ll*t*fp(re[bb[ind+1]],M-2)%M; res=(res+1ll*t*change(bb[ind+1],x)%M)%M; if (!flag) return res; t=1ll*t*fp(son[x],M-2)%M; } else{ if (son[x]) flag=0; break; } } return res; } int main(){ scanf("%d",&n); prew(); for (int i=1; i<=n; i++) scanf("%d",&bb[i]); for (int i=1; i<n; i++){ scanf("%d%d",&u,&v); ++du[u]; ++du[v]; add(u,v); add(v,u); } int now=fac[du[1]]; for (int i=2; i<=n; i++) now=1ll*now*fac[du[i]-1]%M; for (int i=1; i<bb[1]; i++){ ans=(ans+now)%M; now=1ll*now*fp(du[i],M-2)%M*du[i+1]%M; } first(bb[1],0); for (int i=1; i<=n; i++){ sort(g[i].begin(),g[i].end()); int ttt=0; for (vector<int>::iterator it=g[i].begin(); it!=g[i].end(); it++) op[*it]=++ttt; zkw::prew(i); } ans=(ans+change(bb[1],0))%M; printf("%d\\n",ans); }
以上是关于XJOI 旅行(树形DP)的主要内容,如果未能解决你的问题,请参考以下文章