[Codeforces]762F - Tree nesting

题目大意:给出一棵n个点的树和一棵m个点的树,问第一棵树有多少个连通子树与第二棵树同构。(n<=1000,m<=12)

做法:先找出第二棵树的重心(可能为边),以这个重心为根,可以避免重复计算,顺便对第二棵树的每个子树算出判同构的哈希值。枚举第一棵树的一个点/边与第二棵树的根对应,用f[i][j][k]表示以j为父亲的i的子树内,选出子树哈希值为k的方案数,合并的时候用状压DP。前两维合在一起是O(n)级别的,所以总复杂度是O(nm*2^m)。

代码:

#include<cstdio>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<‘0‘||c>‘9‘);
    for(x=c-‘0‘;(c=getchar())>=‘0‘&&c<=‘9‘;)x=x*10+c-‘0‘;
    return x;
}
#define MN 1000
#define MM 13
#define MOD 1000000007
struct edge{int nx,t;}e[MN*2+MM*4+5];
int h[MN+5],H1[MM+5],H2[MM+5],en;
int m,s[MM+5],rts,rtx,rty,t[MM+5],cnt;
vector<int> v[MM+5],vv[MM+5];
map<long long,int> mp;
int f[MN+5][MN+5][MM+5];
inline void ins(int*h,int x,int y)
{
    e[++en]=(edge){h[x],y};h[x]=en;
    e[++en]=(edge){h[y],x};h[y]=en;
}
void dfs(int x,int fa)
{
    s[x]=1;
    int mx=0;
    for(int i=H1[x];i;i=e[i].nx)if(e[i].t!=fa)
    {
        dfs(e[i].t,x);
        s[x]+=s[e[i].t];
        mx=max(mx,s[e[i].t]);
    }
    mx=max(mx,m-s[x]);
    if(mx<rts)rts=mx,rtx=x,rty=0;
    else if(mx==rts)rty=x;
}
void solve(int x,int fa)
{
    long long hash=0;
    for(int i=H2[x];i;i=e[i].nx)if(e[i].t!=fa)
    {
        solve(e[i].t,x);
        vv[x].push_back(t[e[i].t]);
    }
    sort(vv[x].begin(),vv[x].end());
    for(int i=0;i<vv[x].size();++i)hash=hash*14+vv[x][i];
    t[x]=mp[hash]?mp[hash]:(v[++cnt]=vv[x],mp[hash]=cnt);
}
inline void rw(int&a,int b){if((a+=b)>=MOD)a-=MOD;}
int cal(int x,int fa,int t)
{
    if(f[x][fa][t])return f[x][fa][t]-1;
    int *F=new int[1<<v[t].size()];
    for(int i=F[0]=1;i<1<<v[t].size();++i)F[i]=0;
    for(int i=h[x];i;i=e[i].nx)if(e[i].t!=fa)
        for(int j=1<<v[t].size();j--;)
            for(int k=0;k<v[t].size();++k)
                if(!(j&(1<<k))&&(!k||(j&(1<<k-1))||v[t][k]!=v[t][k-1]))
                    rw(F[j|(1<<k)],1LL*F[j]*cal(e[i].t,x,v[t][k]));
    f[x][fa][t]=F[(1<<v[t].size())-1]+1;delete F;
    return f[x][fa][t]-1;
}
int main()
{
    int n,i,j,ans=0;
    for(n=read(),i=1;i<n;++i)ins(h,read(),read());
    for(m=read(),i=1;i<m;++i)ins(H1,read(),read());
    rts=m;dfs(1,0);
    if(rty)
    {
        if(rtx>rty)swap(rtx,rty);
        for(i=1;i<=m;++i)for(j=H1[i];j;j=e[j].nx)
            if(i<e[j].t&&(i!=rtx||e[j].t!=rty))ins(H2,i,e[j].t);
        ins(H2,rtx,++m);ins(H2,rty,m);rtx=m;
    }
    else for(i=1;i<=m;++i)for(j=H1[i];j;j=e[j].nx)if(i<e[j].t)ins(H2,i,e[j].t);
    solve(rtx,0);
    if(rty)for(i=1;i<=n;++i)for(j=h[i];j;j=e[j].nx)if(i<e[j].t)
    {
        rw(ans,1LL*cal(i,e[j].t,v[t[m]][0])*cal(e[j].t,i,v[t[m]][1])%MOD);
        if(v[t[m]][0]!=v[t[m]][1])
        rw(ans,1LL*cal(i,e[j].t,v[t[m]][1])*cal(e[j].t,i,v[t[m]][0])%MOD);
    }else;
    else for(i=1;i<=n;++i)rw(ans,cal(i,0,t[rtx]));
    printf("%d",ans);
}
时间: 2024-11-16 22:51:57

[Codeforces]762F - Tree nesting的相关文章

Educational Codeforces Round 17F Tree nesting

来自FallDream的博客,未经允许,请勿转载, 谢谢. 给你两棵树,一棵比较大(n<=1000),一棵比较小(m<=12) 问第一棵树中有多少个连通子树和第二棵同构. 答案取膜1e9+7 考虑在大树中随便选个根,然后在小的那棵那里枚举一个根作为大树中深度最小的节点(注意哈希判同构) 然后f[x][y]表示大树的x节点作为小树的y节点的方案数. 如果y是叶子结点,f[x][y]=1 否则要用x的儿子中的一些节点表示y的所有叶子节点. 考虑一个状压dp,g[i][s]表示前i个儿子表示了小树的

Codeforces 570D TREE REQUESTS dfs序+树状数组 异或

http://codeforces.com/problemset/problem/570/D Tree Requests time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Roman planted a tree consisting of?n?vertices. Each vertex contains a lowercase

Codeforces 570D Tree Requests(Dsu On the Tree)

题目链接 Tree Requests 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 7 typedef long long LL; 8 9 const int N = 500010; 10 const int A = 31; 11 12 int cntf[N], sz[N], h[N]; 13 vector <i

Codeforces 23E Tree(树型DP)

题目链接 Tree dp[x][i]表示以x为根的子树中x所属的连通快大小为i的时候 答案最大值 用dp[x][j] * dp[y][k] 来更新dp[x][j + k]. (听高手说这类题的套路其实都差不多) 因为这题输出数据会很大所以用Java-- QAQ 1 import java.util.*; 2 import java.io.*; 3 import java.math.*; 4 5 public class Main{ 6 static final int maxn = 710; 7

Codeforces 570D TREE REQUESTS dfs序+树状数组

链接 题解链接:点击打开链接 题意: 给定n个点的树,m个询问 下面n-1个数给出每个点的父节点,1是root 每个点有一个字母 下面n个小写字母给出每个点的字母. 下面m行给出询问: 询问形如 (u, deep) 问u点的子树中,距离根的深度为deep的所有点的字母能否在任意排列后组成回文串,能输出Yes. 思路:dfs序,给点重新标号,dfs进入u点的时间戳记为l[u], 离开的时间戳记为r[u], 这样对于某个点u,他的子树节点对应区间都在区间 [l[u], r[u]]内. 把距离根深度相

Codeforces 375D Tree and Queries(DFS序+莫队+树状数组)

题目链接  Tree and Queries 题目大意  给出一棵树和每个节点的颜色.每次询问vj, kj 你需要回答在以vj为根的子树中满足条件的的颜色数目, 条件:具有该颜色的节点数量至少为kj. (莫队居然可以过) 首先转DFS序,这样就变成了区间查询. 然后直接套用莫队,求出每次询问状态下的t[],t[k]表示当前区间内拥有k个节点的颜色数量. 然后统计t[k] + t[k + 1], ..., t[MAX]即可,这个过程用树状数组维护. #include <bits/stdc++.h>

Codeforces 911F Tree Destruction(贪心 &amp;&amp; 树的直径)

题目链接  Tree Destructi 题意  给定一棵树,每次可以选定树上的两个叶子,并删去其中的一个.答案每次加上两个选定的叶子之间的距离. 求最后答案的最大值. 首先求出树的某一条直径,令其端点分别为L, R. 把L看成树的根,那么R一定是叶子结点. 对于那些非直径上的点,离他们最远的点肯定是L或R中的一个(可能也有其他的,但是L或R肯定已经最大了) 所以依次把这些非直径上的点删掉,删掉的时候在L和R中选择一个就行了. 最后把直径删掉即可. 时间复杂度$O(nlogn)$  (应该是可以

Codeforces 932D - Tree

932D - Tree 思路: 树上倍增 anc[i][u]:u的2^i祖先 mx[i][u]:u到它的2^i祖先之间的最大值,不包括u pre[i][u]:以u开始的递增序列的2^i祖先 sum[i][u]:以u开始递增序列从u到2^i祖先的和,不包括u 代码: #include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mem(a,b) memset(a,b,s

CodeForces 375D Tree and Queries 莫队||DFS序

Tree and Queries 题意:有一颗以1号节点为根的树,每一个节点有一个自己的颜色,求出节点v的子数上颜色出现次数>=k的颜色种类. 题解:使用莫队处理这个问题,将树转变成DFS序区间,然后就是开一个数据记录下出现次数为k次的颜色数目,查询的时候直接返回这个数组中的对应的值就行了. 注意的就是记得将节点的颜色传递给dfs序对应位置的颜色. 这个忘记了找了好久的bug. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3