2023.11.4NOIP模拟赛第一题 花菖蒲

题意:
判断是否存在一棵树,满足它有 aaa 个一度点和 bbb 个三度点,如果存在请给出一个节点数不超过 200020002000 的构造,否则输出 000

首先我们来分点考虑:

  • 对于 Subtask2:b=0Subtask2:b=0Subtask2b=0 的情况

    • 考虑如下情况:
      1. 对于 a=0a=0a=0 的情况,直接输出 111 即可(即只有一个节点)。
      2. 对于 a=1a=1a=1a=3a=3a=3 的情况,我们无法构造,输出 000
      3. 对于不同于以上三种的情况,考虑菊花图。即 111 号节点向外发散 aaa 个点,可以构造出满足题意的树
  • 对于 Subtask3:b=a−2Subtask 3 : b=a-2Subtask3:b=a2 的情况

    • 可以构造出这样一棵树:
      • 对于奇数号的节点,它们都有左右儿子,而偶数号节点它们左右儿子都没有。即形如 111 号节点的左儿子为 222 号节点,右儿子为 333 号节点, 111 号节点的右儿子 333 号节点又有左儿子为 444 号节点,右儿子为 555 号节点,以此类推……
      • 通过这样的方式,我们发现其实只有 111 号节点是附加的,是一个二度点,其他的点要么是一度点,要么是三度点。这样构造下来刚好有 aaa 个一度点和 bbb 个三度点。那么对于 0≤a,b≤2000 \leq a,b \leq 2000a,b200 的数据,这棵树的节点不会超过 401401401 个点,符合题意。
  • 现在来考虑对于全部的数据:
    我们可以从 Subtask3:b=a−2Subtask 3 : b=a-2Subtask3:b=a2 得到启发,只要首先满足三度点的个数要求,构造出一棵形如 Subtask3Subtask3Subtask3 的数据的树,再来考虑剩余的一度点。

    • 考虑如下情况:
      1. 无解情况
      可以注意到除了两个 111 度点,树上的点的平均度数为 222 。所以显然有 a<b+2a < b + 2a<b+2 的时候无解。
      2. 剩下的一度点节点数为 111
      它既不能放在一度点,因为毫无作用,也不能放在二度点,因为会使二度点变成三度点,也不能放在三度点,因为会使三度点的个数减少。所以无法构造,直接输出 000
      3. 剩下的一度点个数大于 111
      直接往一号节点处放置即可。即往一号节点(唯一的二度点)添加儿子,它属于一个公用点,哪里需要往哪搬,它并不计入需要的一度点或三度点个数中,只要添加的不止一个儿子就行。

    对于上面这种构造方法,其实也符合 Subtask1Subtask1Subtask1 的情况,那么最终的总的节点个数为 a+b+1a+b+1a+b+1 个点,只要有解就一定不会超过限制。时间复杂度 O(a+b)O(a+b)O(a+b)

不知我的做法有没有问题,考场通过了,欢迎来hack。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll a,b;
int main() 
{
    // freopen("irises.in","r",stdin);
    // freopen("irises.out","w",stdout);
    cin>>a>>b;
    if(a<b+2)
    {
        cout<<0;
        return 0;
    }
    if(b==0)
    {
        if(a==0)
        {
            cout<<1;
            return 0;
        }
        if(a!=3)
        {
            cout<<a+1<<endl;
            for(int i=2;i<=a+1;i++)
            {
                cout<<1<<" "<<i<<endl;
            }
            return 0;
        }
    }
    if(b==a-2)
    {
        ll cnt=0;
        cout<<a+b+1<<endl;
        for(int i=1;i<=a+b-1;i+=2)
        {
            if(cnt<a+b) cout<<i<<" "<<i+1<<endl,cnt++;
            if(cnt<a+b) cout<<i<<" "<<i+2<<endl,cnt++;
        }
        return 0;
    }
    if(a-b-2<2)
    {
        cout<<0;
        return 0;
    } 
    ll cnt=0;
    cout<<a+b+1<<endl;
    for(int i=1;i<=b+b+2-1;i+=2)
    {
        if(cnt<b+b+2) cout<<i<<" "<<i+1<<endl,cnt++;
        if(cnt<b+b+2) cout<<i<<" "<<i+2<<endl,cnt++;
    }
    cnt++;
    for(int i=1;i<=a-b-2;i++)
    {
        cout<<1<<" "<<cnt+i<<endl;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值