树上问题 ---- E. Fib-tree(斐波那契数的性质 + 暴力模拟 + 认真计算复杂度)

题目大意:

一个树是 F I B FIB FIB树得是节点个数为斐波那契数且(注意这个且)!!此外满足下面条件一个:

1.只有一个点
2.可以切一条边使得分出的两个子树都是 F I B FIB FIB树。
给你一棵树,问是否是 F I B FIB FIB
注意这是个递归定义来着


解题思路:

  1. 首先我们知道 n = f i b [ i ] = f i b [ i − 1 ] + f i b [ i − 2 ] n=fib[i]=fib[i-1]+fib[i-2] n=fib[i]=fib[i1]+fib[i2]
  2. 那么 f i b [ i ] fib[i] fib[i]只能划分成 f i b [ i − 1 ] 和 f i b [ i − 2 ] fib[i-1]和fib[i-2] fib[i1]fib[i2]
  3. 我们转化一下 f i b [ i ] = 2 ∗ f i b [ i − 2 ] + f i b [ i − 3 ] < 3 ∗ f i b [ i − 2 ] fib[i]=2*fib[i-2]+fib[i-3]<3*fib[i-2] fib[i]=2fib[i2]+fib[i3]<3fib[i2]
  4. 那么我们知道一个树里面最多有两个子树的大小是 f i b [ i − 2 ] fib[i-2] fib[i2]那么我们要选择切哪条边呢?
  5. 其实选择哪条边都一样因为, f i b [ i − 1 ] = f i b [ i − 2 ] + f i b [ i − 3 ] fib[i-1]=fib[i-2]+fib[i-3] fib[i1]=fib[i2]+fib[i3]!!那么就是两条边最终还是会被切掉的,所以随便切一条就可以了
  6. 那么我们暴力去模拟题意就可以了?时间复杂度是 O ( n 2 ) ? ? O(n^2)?? O(n2)??
    在这里插入图片描述
    很明显不是,因为每次分裂最大递归 l o g = 27 log=27 log=27层,斐波那契的层数!!
    时间复杂度是 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
int n;
struct node {
    int nxt, to;
}edge[maxn];
int head[maxn], cnt;
bool tag[maxn];

inline void add(int from, int to) {
    edge[cnt] = {head[from],to};
    head[from] = cnt++;
}

//........................
int f[maxn];
bool vis[maxn];
inline void init() {
    ms(head,-1);
    f[0] = f[1] = 1;
    vis[1] = 1;
    for(int i = 2; ; ++ i) {
        f[i] = f[i-1] + f[i-2];
        vis[f[i]] = 1;
        if(f[i]>200000) break;
    }
}

//........................
int siz[maxn];
int pos, posfa;
inline void dfs(int u, int fa,int num) {
    siz[u] = 1;
    for(int i = head[u]; ~i; i = edge[i].nxt) {
        int v = edge[i].to;
        if(v == fa || tag[i]) continue;
        dfs(v,u,num);
        if(~pos) return;
        if(vis[siz[v]]&&vis[num-siz[v]]) {
            tag[i] = tag[i^1] = 1;
            pos = v;
            posfa = u;
            return;
        }
        siz[u] += siz[v];
    }
}

inline void Div(int now, int num) {
    if(num == 1) return;
    pos = -1, posfa = -1;
    dfs(now,0,num);
    if(pos==-1) {
        cout << "NO\n";
        exit(0);
    }
    int backposfa = posfa, backupnum = num - siz[pos];
    Div(pos,siz[pos]);
    Div(backposfa,backupnum);
}

int main() {
    IOS;
    init();
    cin >> n;
    if(!vis[n]) {
        cout << "NO";
        return 0;
    }
    for(int i = 1; i <= n-1; ++ i) {
        int u, v;
        cin >> u >> v;
        add(u,v);
        add(v,u);
    }
    Div(1,n);
    cout << "YES\n";
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值