本旨:
shyにはツリーがあり、ツリーにはn個のノードがあります。木を染めるために異なる色のkの染料があります。同じ色のすべてのポイントペア(x、y)について、xからyへのパス上のすべてのポイントの色がxおよびyと同じでなければならない場合に限り、配色は合法です。プランの数を数えてください。
質問のアイデア:
各色は接続されたブロックであるため、質問の要件を変換します。
それ以外の場合、要件は満たされていません
したがって、このツリーをkkに分割することを検討できます。k個の接続されたブロックのスキームの数はいくつですか
kkに分割する場合k個の接続されたブロックの各プランにはmmがありますm色の場合、スキームの数は当然次のようになります。Amk A_m ^ kAmK
したがって、ツリーを1 .... k 1 .... kに分割する必要があります。1 。。。。、K用のプログラム通信ブロックの数、各KKk、ans = ans + cal(k)∗ A mk ans = ans + cal(k)* A_m ^ ka n s=a n s+c a l (k )∗AmK
この問題は解決されました
ツリーをk個の接続されたブロックに分割するためのスキームの数を見つける方法は?
2つの方法:
1.树形dp
dp [u] [k] dp [u] [k] d p [ u ] [ k ]は、uをルートとするサブツリーを表し、kkに分割されます。k個の接続されたブロックのスキームの数
次に、状態遷移は明らかに、各uuに対してです。uの子eee
このサブツリーには2つの状況があります。
- 最後に接続されたブロックに組み込む
- 最後に接続されたブロックに収まらない
だからあります:
for(int i=1;i<=min(sz[u],m);i++){
///枚举子树大小
for(int k=1;k<=min(sz[e],m);k++){
if(i+k-1<=m) t[u][i+k-1] = (t[u][i+k-1] + (dp[u][i] * dp[e][k]) )%mod;
}
}
for(int i=1;i<=min(sz[u],m);i++){
///枚举子树大小
for(int k=1;k<=min(sz[e],m);k++){
if(i+k<=m) t[u][i+k] = (t[u][i+k] + (dp[u][i] * dp[e][k]) )%mod;
}
}
for(int k=1;k<=m;k++) dp[u][k] = t[u][k],t[u][k] = 0;
したがって、複雑さはO(n ∗ m)O(n * m)です。O (n∗m ) a
2.2。组合数学
ツリーをkkに分割することを検討してくださいk個の接続されたブロックは木を切り落とすだけです(k − 1)(k-1)(k−1 )エッジ
したがって、n − 1n-1でn−1エッジタイプk− 1 k-1k−1エッジの場合、解の数はC n − 1 k − 1 C_ {n-1} ^ {k-1}です。Cn − 1K - 1
そして、A mk A_m ^ kでAmK掛けるだけ
最後に、コードdfs(1、1)dfs(1,1)を添付しますd f s (1 、1 )した後、DP [1]〜[I] DP [1]〜[I]d p [ 1 ] [ i ]は、i個の接続されたブロックに分割された解の数を表します
コード:
/*** keep hungry and calm CoolGuang! ***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17+7;
const ll maxn =2e5+700;
const ll mod= 1e9+7;
const ll up = 1e13;
const double eps = 1e-9;
template<typename T>inline void read(T &a){
char c=getchar();T x=0,f=1;while(!isdigit(c)){
if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
vector<int>v[maxn];
ll dp[305][305];
ll t[305][305];///代替分组背包
ll sz[maxn];
ll s[2005][2005];
ll cal(ll x,ll y){
if(x<y) return 0;
if(y == 0 || x == y) return s[x][y] = 1;
if(y == 1) return s[x][y] = x%mod;
if(~s[x][y]) return s[x][y];
return s[x][y] = (cal(x-1,y) + cal(x-1,y-1))%mod;
}
void dfs(int u,int fa){
sz[u] = 1;
dp[u][1] = 1;
for(int e:v[u]){
if(e == fa) continue;
///对于新来的任何一个子数 都有与当前合并 和 不与当前合并
dfs(e,u);
for(int i=1;i<=min(sz[u],m);i++){
///枚举子树大小
for(int k=1;k<=min(sz[e],m);k++){
if(i+k-1<=m) t[u][i+k-1] = (t[u][i+k-1] + (dp[u][i] * dp[e][k]) )%mod;
}
}
for(int i=1;i<=min(sz[u],m);i++){
///枚举子树大小
for(int k=1;k<=min(sz[e],m);k++){
if(i+k<=m) t[u][i+k] = (t[u][i+k] + (dp[u][i] * dp[e][k]) )%mod;
}
}
for(int k=1;k<=m;k++) dp[u][k] = t[u][k],t[u][k] = 0;
sz[u] += sz[e];
}
}
ll A[maxn];
int main(){
memset(s,-1,sizeof(s));
read(n);read(m);
for(int i=1;i<=n-1;i++){
int x,y;read(x);read(y);
v[x].push_back(y);
v[y].push_back(x);
}
A[0] = 1;
for(int i=1;i<=m;i++) A[i] = (A[i-1] * (m-i+1))%mod;
ll ans = 0;
for(int i=1;i<=m;i++){
ans = (ans + (cal(n-1,i-1)*A[i])%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}
/***
ababd
abd
***/