最长公共子序列
问题描述:
对一棵有根树执行一次DFS,可以得到一个前序遍历和一个后序遍历, 设它们的最长公共子序列长度和方案数分别是 f,g 。 DFS时可以任意调整子树顺序,不同顺序的DFS会得到不同的前序和后序遍历。 设最长公共子序列长度的最大值是 F ,方案总数是 G 。 即 F=max所有DFS顺序(f) , G=∑所有DFS顺序(g) 。 给你一棵无根树,请你求出将每个节点 u 被设为根时的 Fu,Gu 。 Gu 可能很大所以 mod 998244353 。 对于 100% 的数据, n≤500000 。
分析:
可以树形dp+换根,但kzsn没写对,只写了40分。
以下为大佬qfxl的分析,已得到qfxl的许可,于是,kzsn小菜鸡,便不用写了!
对于以 x 为根的树(如下图,其中 a , b , c 代表以其为根的子树)
ta的前序遍历是: x , a , b , c ;后序遍历是: a , b , c , x 。
我们可以得出结论:一棵有根树的最长公共子序列长度为其叶子节点个数。
那么,F 就解决了,只需要求出叶子节点个数,再记一下它是不是叶子。
然后来讨论 G :
对于上图,不难发现:对于一个红圈而言,任选一个点并不会影响答案,将它命名为“叶链”,即从叶子开始,到第一个度数为3的点之前为止;
对一个非叶链上的点 a ,其答案为:∏(In [ x ] - 1 ) ! ∏ ( Size [ y ] ) * In [ a ] (其中,x为非叶链上点,y为叶链上点,In [ ] 为度数,Size [ ] 为叶链大小);
对一个叶链上的点 a ,其答案为:∏(In [ x ] - 1 ) ! ∏ ( Size [ y ] ) * Len [ a ] / Size [ a ] * In [ a ] (其中,x为非叶链上点,y为叶链上点,In [ ] 为度数,Len [ ] 为点到叶子距离,Size [ ] 为叶链大小);
由于前面有大量重复项,可以把 ∏(In [ x ] - 1 ) ! ∏ ( Size [ y ] ) 提出来计算。
但要注意,n == 1 和 图为一条链 的情况要特判一下
#includeusing namespace std; #define re register int #define LL long long static char buf[1<<20], *p1=buf, *p2=buf; #define getchar() p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<20, stdin), p1==p2)?EOF:*p1++ inline int read() { int x=0; char c=getchar(); while(c<'0'||c>'9')c=getchar(); while('0'<=c&&c<='9')x=x*10+c-48,c=getchar(); return x; } const int N=7e5+5, mo=998244353; inline LL inv(LL x) { int y=mo-2; x=x%mo; LL ret=1; while(y) { if(y&1) ret=ret*x%mo; x=x*x%mo; y>>=1; } return ret; } vector<int>G[N]; int root, n; LL sum=1, fac[N]; int vis[N], in[N], com[N], bel[N], edge; void dfs(int x, int y) { vis[x]++; bel[x]=y; com[y]=vis[x]; int flag=0; for(re v:G[x]) if(!vis[v]&&in[v]==2) { flag=1; vis[v]=vis[x]; dfs(v, y); } if(!flag)sum=1ll*sum*com[y]%mo; } LL f[N], g[N]; void DD(int x, int fa, int cnt) { if(in[x]==1)f[x]=1, g[x]=n; else f[x]=2, g[x]=2ll*cnt*(n-cnt-1)%mo; for(re v:G[x])if(v!=fa)DD(v, x, cnt+1); } int main() { n=read(); fac[0]=1;for(re i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mo; int judge=0; for(re i=1;i i) { int x=read(), y=read(); G[x].push_back(y); G[y].push_back(x); in[x]++; in[y]++; judge=max(judge, max(in[x], in[y])); } if(n==1)return puts("1 1"),0; if(judge<=2) { for(re i=1;i<=n;++i)if(in[i]==1){DD(i, 0, 0);break;} for(re i=1;i<=n;++i) printf("%lld %lld\n", f[i], g[i]); return 0; } for(re i=1;i<=n;++i)if(in[i]==1)dfs(i,++edge),root++; for(re i=1;i<=n;++i)if(!vis[i])sum=sum*fac[in[i]-1]%mo; for(re i=1;i<=n;++i) { int A1=0; LL A2=1; if(!vis[i]) { A1=root; A2=sum*in[i] % mo; } else { A1=root-(vis[i]==1); A2=1ll * sum * inv(com[bel[i]]) % mo * max(1, vis[i]-1) % mo * in[i] % mo; } printf("%d %lld\n", A1, A2); } return 0; }