Codeforces Regular Forestation(樹的重心+樹的同構)

題意:

給出一個樹,讓你選擇一個NodeuNode-u刪掉(根據樹的定義,我們可以知道,只要刪去的該節點的度2\geq2,那麼就可以得到degreeudegree_u個子樹。)
對於剩下的子樹中,如果存在TreesTree_sTreetTree_t,對於TreesTree_s中的 結點對(sis_i,sjs_j),且sis_isjs_j之間有邊相連。若存在一個映射關係ff,使得 TreetTree_t中存在結點對(f(si),f(sj))(f(s_i),f(s_j)),且f(si)f(s_i)f(sj)f(s_j)之間也有邊相連,那麼TreesTree_sTreetTree_t即是identical的。 現在想讓你找到一個點使得這樣的identical的子樹數量最多,並輸出樹的數量。

剛看到題,其實挺懵逼的,簡單分析了一下之後,覺得如果要使得這樣的identical的子樹對越多,那麼肯定是要找度多一些且每一棵分出來的子樹結點要均衡。 然後就不知道該怎麼入手了。
後面看了題解,瞭解到了樹的重心的概念和樹同構的算法(還是基礎太弱了…)


樹的重心

百度百科的定義:樹的重心也叫樹的質心。找到一個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心,刪去重心後,生成的多棵樹儘可能平衡

對這道題來說,很顯然刪掉的點應該是樹的重心,因爲刪去重心後,剩下的樹儘可能地平衡(節點數相近) 且這樣子樹的數量也會增加,這樣identical的樹對理論上應該會比其他切割點的情況大。(筆者只能口胡,感覺上是正確的

找樹的重心

void dfs(int u,int fa){
	siz[u] = 1;	vis[u] = true;
	int maxn = 0;
	for(int i = head[u]; ~i; i = edge[i].nxt){
		Edge &e = edge[i];
		if(!vis[e.v])	dfs(e.v,u);
		siz[u] += siz[e.v];
		maxn = max(maxn,siz[e.v];
	}
	maxn = max(maxn,n - siz[u]);
	if(maxans > maxn){
		update(ans,maxans);
	} 
}


然後再看題目:

對於剩下的子樹中,如果存在TreesTree_sTreetTree_t,對於TreesTree_s中的 結點對(sis_i,sjs_j),且sis_isjs_j之間有邊相連。若存在一個映射關係ff,使得 TreetTree_t中存在結點對(f(si),f(sj))(f(s_i),f(s_j)),且f(si)f(s_i)f(sj)f(s_j)之間也有邊相連,那麼TreesTree_sTreetTree_t即是identical的。

對於這個限制條件該如何轉化?

對於這個題剛開始,我只能想到 兩棵樹要是能identical,那麼他們兩個的結點數一定是一樣的。

#include <bits/stdc++.h>
#define base 2333
using namespace std;
typedef long long ll;
const int maxn = 4e3+5;
const int INF = 0x3f3f3f3f;
const ll seed = 1e6+7;
const ll mod = 998244353;
struct Edge{
    int u,v,nxt;
}edge[maxn<<1];
int head[maxn],tot;
inline void addedge(int u,int v){
    edge[++tot] = {u,v,head[u]};
    head[u] = tot;
}
int siz[maxn];  bool vis[maxn];
int zxNode[maxn];   int id;
inline void dfs(int u,int fa,int &maxans,int n){
    // this function is to find the tree centrio
    vis[u] = true;  siz[u] = 1;
    int TheMax = 0;
    for(int i = head[u]; ~i; i = edge[i].nxt){
        Edge &e = edge[i];
        if(!vis[e.v] && u!=fa){
            dfs(e.v,u,maxans,n);
            siz[u] += siz[e.v];
            TheMax = max(TheMax,siz[e.v]);
        }
    }
    TheMax = max(TheMax,n-siz[u]);
    if(TheMax < maxans){
        zxNode[id=1] = u;
        maxans = TheMax;
    }else if(TheMax == maxans){
        zxNode[++id] = u;
    }
}
ll HashVal[maxn][maxn],tp[maxn];

inline ll Hash_Tree(int u,int fa){
    // u is Node_index,p is subTree's Node num,k is k th Tree
    ll cnt = 0; ll q[maxn];
    for(int i = head[u]; ~i; i = edge[i].nxt){
        Edge &e = edge[i];
        if(vis[e.v] || e.v==fa) continue;
        tp[u] = Hash_Tree(e.v,u);
    }
    for(int i = head[u]; ~i; i = edge[i].nxt){
        Edge &e = edge[i];
        if(vis[e.v] || e.v==fa) continue;
        q[++cnt] = tp[e.v];
    }
    sort(q + 1,q + cnt + 1);   ll now = seed;
    for(int i = 1; i <= cnt; ++i) now = (now*base + q[i]);
    cout << u << ' ' << now*base + seed +1 << endl;
    return tp[u] = now * base + seed +1;
}
int fid[maxn][maxn];
inline void init(int id,int u){
    fid[id][++fid[id][0]] = u;  vis[u] = 1;
    for(int i = head[u]; ~i; i = edge[i].nxt){
        Edge&e = edge[i];
        if(!vis[e.v])   init(id,e.v);
    }
}
inline void check_ans(int u,int ans,int &res,int n){
    int num = 0;
    memset(HashVal,0,sizeof(HashVal));  memset(vis,0,sizeof(vis));  vis[u] = 1;
    for(int i = 1; i <= n; ++i){
        if(!vis[i]) ++num,fid[i][0] = 0,init(num,i);
    }
    memset(vis,0,sizeof(vis));  vis[u] = 1;
    for(int i = 1; i <= num; ++i){
        if(i > 1 && fid[i][0] != fid[i-1][0])   return;
        for(int j = 1; j <= fid[i][0]; ++j){
            HashVal[i][j] = Hash_Tree(fid[i][j],0);
            cout << "---------------------------" << endl;
 //           cout << HashVal[i][j] << endl;
        }
        sort(HashVal[i]+1,HashVal[i]+1+fid[i][0]);
        if(i > 1){
            int q = 0;  while(q <= fid[i][0]) if(HashVal[1][++q]!=HashVal[i][q])return;
        }
    }
    res = max(res,num);
    return;
}
int main()
{
    ios::sync_with_stdio(false);    cin.tie(0); cout.tie(0);
    memset(head,-1,sizeof(head));
    int n;  cin >> n;   for(int i = 1,u,v; i < n; ++i)  cin >> u >> v,addedge(u,v),addedge(v,u);
    int ans = INF;  dfs(1,0,ans,n); int res = -1;
    for(int i = 1; i <= min(2,id); ++i){
        check_ans(zxNode[i],ans,res,n);
    }
    cout << res << endl;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章