#include <fstream>
#include <vector>
#include <cstring>
#include <map>
#include <algorithm>
#define Pe pair <int, int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
class InputReader {
public:
InputReader() {}
InputReader(const char *file_name) {
input_file = fopen(file_name, "r");
cursor = 0;
fread(buffer, SIZE, 1, input_file);
}
inline InputReader &operator >>(int &n) {
while (buffer[cursor] < '0' || buffer[cursor] > '9') {
advance();
}
n = 0;
while ('0' <= buffer[cursor] && buffer[cursor] <= '9') {
n = n * 10 + buffer[cursor] - '0';
advance();
}
return *this;
}
private:
FILE *input_file;
static const int SIZE = 1 << 17;
int cursor;
char buffer[SIZE];
inline void advance() {
++ cursor;
if (cursor == SIZE) {
cursor = 0;
fread(buffer, SIZE, 1, input_file);
}
}
};
InputReader cin("divizori2.in");
ofstream cout("divizori2.out");
const int MaxN = 100005;
vector <int> G[MaxN], GrNode[MaxN], divisor, CurrSubtree[MaxN];
vector <int> MiniG0[MaxN];
vector <int> MiniG1[MaxN];
map <vector <int>, int> STMap;
int SubTree[MaxN], Group[MaxN], father[MaxN];
bool used[MaxN], used2[MaxN], used3[MaxN];
int n, GroupNr, Cnt, MaxLev, FarNode, MapTop, used3cnt, GrSz;
bool Ans, ret;
void DivInit(int n) {
for (int i = 2; i < n; ++i) {
if (n % i == 0) {
divisor.push_back(i);
}
}
}
void Dfs2(int node, int CurrGr, int DivSz) {
if (GrSz == DivSz) return;
used3[node] = true;
++used3cnt;
Group[node] = CurrGr;
++GrSz;
for (auto nxt: G[node]) {
if (used3[nxt]) continue;
Dfs2(nxt, CurrGr, DivSz);
}
}
void Dfs(int node, int DivSz, int parent, bool &Ans) {
used[node] = true;
for (auto nxt: G[node]) {
if (used[nxt]) continue;
Dfs(nxt, DivSz, node, Ans);
SubTree[node] += SubTree[nxt];
if (SubTree[node] > DivSz) {
Ans = false;
}
else if (SubTree[node] == DivSz) {
GrSz = 0;
int switchused = false;
if (!used3[parent]) {
switchused = true;
used3[parent] = true;
}
if (used3[node]) {
continue;
}
Dfs2(node, ++GroupNr, DivSz);
if (GrSz < DivSz) {
Ans = false;
}
SubTree[node] = 0;
if (switchused) {
used3[parent] = false;
}
}
}
}
inline void QueryInit() {
Ans = true;
for (int i = 1; i <= n; ++i) SubTree[i] = 1;
GroupNr = 1;
used3cnt = 0;
memset(Group, 0, sizeof Group);
memset(used, 0, sizeof used);
memset(used3, 0, sizeof used3);
}
void GrNodeInit() {
for (int i = 1; i <= GroupNr; ++i) {
GrNode[Group[i]].push_back(i);
}
}
void BuildGraph(vector <int> CurrG[], int CurrGroup) {
for (auto it: GrNode[CurrGroup]) {
if (Group[father[it]] == CurrGroup) {
CurrG[it].push_back(father[it]);
CurrG[father[it]].push_back(it);
}
}
}
void FindFarthest(vector <int> CurrG[], int node, int level = 1) {
used2[node] = true;
if (level > MaxLev) {
MaxLev = level;
FarNode = node;
}
for (auto nxt: CurrG[node]) {
if (used2[node]) continue;
FindFarthest(CurrG, nxt, level + 1);
}
}
void CreateDiam(vector <int> CurrG[], vector <int> diam, int node) {
used2[node] = true;
if (node == FarNode) ret = true;
for (auto nxt: CurrG[node]) {
if (used2[node]) continue;
CreateDiam(CurrG, diam, nxt);
if (ret) {
diam.push_back(nxt);
return;
}
}
}
Pe FindRts(vector <int> CurrG[]) {
MaxLev = 0;
memset(used2, 0, sizeof used2);
FindFarthest(CurrG, 1);
int Rt = FarNode;
MaxLev = 0;
memset(used2, 0, sizeof used2);
FindFarthest(CurrG, Rt);
ret = false;
vector <int> diam;
memset(used2, 0, sizeof used2);
CreateDiam(CurrG, diam, Rt);
diam.push_back(Rt);
if (diam.size() % 2 == 1) {
return mp(diam.size() / 2 + 1, -1);
}
else {
return mp(diam.size() / 2, diam.size() / 2 + 1);
}
}
void CreateConfig(vector <int> CurrG[], int node) {
used[node] = true;
for (auto nxt: CurrG[node]) {
if (used[nxt]) continue;
CreateConfig(CurrG, nxt);
CurrSubtree[node].push_back(STMap[CurrSubtree[nxt]]);
}
sort(CurrSubtree[node].begin(), CurrSubtree[node].end());
if (!STMap[CurrSubtree[node]]) {
STMap[CurrSubtree[node]] = ++MapTop;
}
}
inline void IsomInit() {
for (int i = 1; i <= n; ++i) CurrSubtree[i].clear();
//STMap.clear();
//MapTop = 0;
}
bool Isomorph(vector <int> G1[], vector <int> G2[]) {
Pe ConfRt1, ConfRt2;
STMap.clear();
MapTop = 0;
Pe Rt1 = FindRts(G1);
IsomInit();
CreateConfig(G1, Rt1.fi);
ConfRt1.fi = STMap[CurrSubtree[Rt1.fi]];
if (Rt1.se != -1) {
IsomInit();
CreateConfig(G1, Rt1.se);
ConfRt1.se = STMap[CurrSubtree[Rt1.se]];
}
Pe Rt2 = FindRts(G2);
IsomInit();
CreateConfig(G2, Rt2.fi);
ConfRt2.fi = STMap[CurrSubtree[Rt2.fi]];
if (Rt2.se != -1) {
IsomInit();
CreateConfig(G2, Rt2.se);
ConfRt2.se = STMap[CurrSubtree[Rt2.se]];
}
if (Rt1.se == -1 and Rt2.se == -1) {
return ConfRt1.fi == ConfRt2.fi;
}
else if (Rt1.se == -1) {
return ConfRt1.fi == ConfRt2.fi or ConfRt1.fi == ConfRt2.se;
}
else if (Rt2.se == -1) {
return ConfRt1.fi == ConfRt2.fi or ConfRt2.se == ConfRt2.fi;
}
else {
return ConfRt1.fi == ConfRt2.fi or ConfRt1.fi == ConfRt2.se or ConfRt1.se == ConfRt2.fi or ConfRt1.se == ConfRt2.se;
}
}
inline vector <int> *MiniG(vector <int> MiniG0[], vector <int> MiniG1[], bool typ) {
if (!typ) return MiniG0;
return MiniG1;
}
bool Check() {
int line = 0;
//vector <int> MiniG0[MaxN];
//vector <int> MiniG1[MaxN];
for (int i = 0; i <= n; ++i) {
MiniG0[i].clear();
MiniG1[i].clear();
}
GrNodeInit();
BuildGraph(MiniG(MiniG0, MiniG1, 0), 1);
for (int i = 2; i <= GroupNr; ++i) {
line = !line;
BuildGraph(MiniG(MiniG0, MiniG1, line), i);
//if (!Isomorph(MiniG(MiniG0, MiniG1, 0), MiniG(MiniG0, MiniG1, 1))) {
vector <int> *G1 = MiniG(MiniG0, MiniG1, 0);
vector <int> *G2 = MiniG(MiniG0, MiniG1, 1);
if (!Isomorph(G1, G2)) {
return false;
}
}
return true;
}
int main() {
cin >> n;
Cnt = 2;
if (n == 1) {
cout << 1 << '\n';
return 0;
}
for (int i = 1; i <= n - 1; ++i) {
int a, b;
cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
DivInit(n);
for (auto CurrDiv: divisor) {
QueryInit();
Dfs(1, CurrDiv, 0, Ans);
if (!Ans or used3cnt < n) continue;
Cnt += Check();
}
cout << Cnt << '\n';
return 0;
}