トピックへのリンク:https://ac.nowcoder.com/acm/contest/884/G
タイトル効果:ツリー\(\)を考えるが、その後、\(T \)尋問時間を与える、と\(A \)が接続されているどのように多くの部分グラフツリー\(B_i \)同型を尋ねました。\(| A | \当量2000、T \当量10000、| B_i | \当量12 \)
解決策:この問題は、実際に762Fの拡張バージョンをCodeforcesされており、この問題についての説明は、ここにスタンプしてください
この質問は聞いて、そう\(12 \)を超えないのすべてのポイントの前の列挙を検討する前に、この質問のアプローチに似ていますが、また、ツリーの前処理ツリーDPの最小表現の後に、しかしとして多くの千10倍もあるので、木とは、その最小限の表現を見つけます。木の前処理のための全ての条件を満足する方法を、私の方法は、点として現在のツリー\のサイズ(\ N-)、最初の\(N + 1 \)のものとする現在のポイントまたはその祖先息子はツリーを追加し、そしてツリーは、サイズ\(12 \)に達するまで再帰的に続けています。このような前処理後のポイントの数は\(8000 \)ヶ月未満\(12 \)ツリーを超えていないでしょう。次のステップは、[I] [j]が\に数が(私は\)番号\(J \)同型ツリーとサブツリーのルート・ノードであることを示しセットF、DPのツリー\(\)であります次に\(ANS [J] = \作る sum_ {i = 1} ^ {n}はF [I] [J] \)、 各課題に対する答えが\されるため(\和ANS [J] \)、 ここでルートの数(Bの\)の異なる点に対応する\(J \)ツリー\。
また、前処理の時点で、我々はまた、プリアウト時に番号の後に新しい木の数として、ツリーのルートに数\(j個\)することができます\(私は\)ツリーマージの根の息子である、など合わせた関係未満\(14,000 \)基です。列挙は、すべてのそのような組み合わせの関係が算出されるDPツリー\(\)により行うことができる、最適化\のこの部分の時間的複雑度(O(14000n)\)
#include <ビット/ STDC ++ H> 使用して 名前空間STDを、 #define N 2001 の#define M 1 << 12 の#define MM 8001 の#define NN 16773121 の#define MOD十億七 INT LEN(INT X){ 返す 32 - __builtin_clz(x);} int型連合(int型のx、int型の Y){ リターン(X << LEN(Y))| Y;} int型のCNT。 設定 < 整数 > ID [ 13 ]。 int型のユニ[MM] [MM]。 int型のnum_to_id [NN]。 int型id_to_num [MM]。 INT F [N] [MM]。 ベクター < INT > ID [ 13 ]。 構造体ツリー { INT SZ [N]。 int型N、ANS [NN]。 ベクター < INT > D [N]。 ベクター < INT > MP [MM]。 ボイドリード() { scanf関数(" %のD "、&N) 以下のために(int型私= 1 ; iが<= N; iが++ ) D [i]を.clear(); 以下のための(int型私は=2 ; iが<= N; iが++ ) { int型V、Uと、 scanf関数(" %d個の%d個"、&U&V); D [U] .push_back(V)。 D [V] .push_back(U)。 } } int型 DFS(INT CUR、INT PRE) { SZ [CUR] = 1 。 int型 RES = 1 ; ベクトル < int型 > TMP; 以下のための(自動NXT:D [CUR])の場合(!NXT = 前) SZ tmp.push_back(DFS(NXT、CUR))、[CUR]+ = SZ [NXT]。 ソート(tmp.begin()、tmp.end()); 以下のための(自動X:TMP)RES = 連合(RES、x)は、 RES << = 1 。 もし CNT ++、MP [CNT] = tmpに、id_to_num [CNT] = resを、num_to_id [RES] =(num_to_id [RES]!)CNT; 以下のために(int型 i = 0 ; iは++; iがtmp.size()< ) { int型の R = 1 。 以下のための(int型 J = 0 ; jの<tmp.size(); J ++)の場合(!J = I) R = 連合(R、TMP [J]); R << = 1。 UNI [num_to_id [R] [num_to_id [TMP [I]]] = num_to_id [RES]。 } ID [SZ [CUR](num_to_id [RES])を挿入します。 リターンのres; } ボイドのgetId() { ため(int型 i = 1 ; iが<= N; iが++ ) DFS(I、0 ); } ボイド DP2(INT CUR、INT PRE) { SZ [CUR] = 1 。 F [CUR] [ 1 ] = 1 。 以下のための:(D [CUR]オートNXT)の場合(NXT =!PRE) { DP2(NXT、CUR)。 以下のために(int型 I =分(12、SZ [CUR]); I> = 1 ; i-- ) のための(自動II:ID [I]) { int型 V = F [CUR] [II]。 もし(!v)を続けます。 用(INT J = 1 ; J <=分(12 -i、SZ [NXT]); J ++ ) のための(自動JJ:ID [J]) ([UNI [II] [JJ] [CUR] F + = V * F [NXT] [JJ]%MOD)%= MOD。 } SZ [CUR] + =[NXT] SZ。 } のために(int型 i = 1 ; iが=分(< 12、SZ [CUR])を、iは++ ) のための(自動II:ID [i])と (ANS [II] + = F [CUR] [II])%= MOD。 } } S、T。 設定 < 整数 > 秒; INT FA [ 13 ]。 ベクター < INT > D [ 13 ]。 ボイドファック(INT CUR、INT PRE) { FA [CUR] = 予備。 以下のための(int型 I = 1;私は= < 12 ; iは++ ) Tdの[I] = Dの[I]。 TN = CUR。 もし(CUR == 12){T.dfs(1、0); 返す;} int型 X = CUR。 一方、(X!= 0 ) { D [X] .push_back(CUR + 1 )。 ファック(CUR + 1 、X)。 D [X] .pop_back()。 X = FA [X]。 } } int型のmain() { ファック(1、0 ); 以下のために(int型 i = 1 ; iは<= 12 ; I ++ ) のための(自動J:ID [i])とID [i]の.push_back(J)。 S.read(); S.DP2(1、0 ); int型のトン。 scanf関数(" %のD "、&T)。 一方、(t-- ) { T.read()。 int型 ANS = 0 ; s.clear(); 以下のために(int型 I = 1 ; I <= Tnを、iは++ ) s.insert(T.dfs(I、0 ))。 以下のための(自動X:S)(ANS + = S.ans [num_to_id [X]])%= MOD。 printf(" %d個の\ n " 、ANS)。 } 戻り 0 。 }