*题解:CF1491E Fib-tree
解析
首先不难发现一次合法的切割操作必定将顶点数为 \(F_n\) 原树分隔成顶点数为 \(F_{n - 1}\) 和 \(F_{n - 2}\) 的两部分。但是这样的切割方式有多种,遍历每种切割方式并往下递归的复杂度是不可接受的。但事实上,我们可以证明,若存在一种合法切割方式使得切割后两连通块仍是 Fib-tree,则所有切割后顶点数合法的方案都合法。
具体地,大小 \(\le F_3\) 的 Fib-tree 显然满足这个条件。假设对于大小 \(\le F_n\) 的 Fib-tree 也满足条件。那么对于大小为 \(F_{n + 1}\) 的 Fib-tree,其必定存在一种合法切割方式使得切割边 \(e_1\) 后变为一个大小为 \(F_{n}\) 的 Fib-tree 和一个大小为 \(F_{n - 1}\) 的 Fib-tree。若这种切割方式切割出来的是大小为 \(F_{n - 1}\) 的子树,其为 Fib-tree,则其余切割方式所要切割的边 \(e\) 都在该子树内,否则不满足连通块大小关系。在大小为 \(F_{n - 1}\) 的子树内切 \(e_2\),又必定将其分隔成大小为 \(F_{n - 2}\) 和 \(F_{n - 3}\) 的两部分,又因为 \(e_1,e_2\) 的切割顺序不影响答案且我们保证了大小 \(\le F_n\) 的 Fib-tree 满足条件,故先切割 \(e_2\) 也是合法的。第一步切出的是大小为 \(F_{n - 2}\) 的子树的情况同理。
故直接找到一个切割后两连通块大小合法的边进行切割,往下递归判断即可。
每次递归问题规模由 \(F_{i}\) 变为 \(F_{i - 1}\) 和 \(F_{i - 2}\),故只会递归 \(i\) 层,每个点至多被遍历 \(i\) 次,又由于 \(F_n = O(\phi^n),\phi = \dfrac{1 + \sqrt{5}}{2}\),故时间复杂度为 \(O(n\log n)\)。
想到了的地方 / 探索过的思路
划分后的两连通块大小唯一。
没想到的地方
可以任意划分。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 4e5 + 5;
vector<int> t[N];
int siz[N];
int f[N],rev[N],vis[N];
int n;
void dfs(int x,int fa){
siz[x] = 1;
for(int nx : t[x])if(nx != fa){
dfs(nx,x);
siz[x] += siz[nx];
}
}
pii get(int x,int fa,int sz){
if(siz[x] == sz){
return {x,fa};
}
pii res = {-1e9,-1e9};
for(int nx : t[x])if(nx != fa && !vis[nx]){
res = max(res,get(nx,x,sz));
}
return res;
}
void getsz(int x,int fa){
siz[x] = 1;
for(int nx : t[x])if(nx != fa && !vis[nx]){
getsz(nx,x);
siz[x] += siz[nx];
}
}
void vs(int x,int fa){
vis[x] = true;
for(int nx : t[x])if(nx != fa && !vis[nx]){
vs(nx,x);
}
}
bool sol(int x,int fa,int sz){
if(x < 0) return false;
if(sz == 1) return true;
int k = rev[sz];
siz[fa] = 0;
getsz(x,fa);
pii p1 = get(x,fa,f[k - 1]),p2 = get(x,fa,f[k - 2]);
bool f1 = false,f2 = false;
if(p1.first >= 1){
f1 = sol(p1.first,p1.second,f[k - 1]),f2 = sol(x,fa,f[k - 2]);
}else if(p2.first >= 1){
f1 = sol(p2.first,p2.second,f[k - 2]),f2 = sol(x,fa,f[k - 1]);
}
vs(x,fa);
return f1 && f2;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
cin>>n;
f[0] = f[1] = 1;
for(int i=2;;i++){
f[i] = f[i - 1] + f[i - 2];
if(f[i] > n) break;
rev[f[i]] = i;
}
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
t[u].push_back(v);
t[v].push_back(u);
}
bool f = sol(1,0,n);
cout<<(f ? "YES" : "NO")<<'\n';
return 0;
}

浙公网安备 33010602011771号