2019-2020 ICPC Asia Hong Kong Regional Contest

G. Game Design

题意:构造一棵节点带权值的树。构造完成后,树上的每个叶子节点都会出现一个怪物,怪物会自底向上进攻根节点,现在你能够在任意个节点建造防御塔,防御塔的建造花费是你构造是给出的点权 ci ,以建造防御塔后的节点为根的子树中的所有怪物都会被防御塔杀死。现在给定一个正整数 K 代表以最小花费修建防御塔拦截杀死怪物的不同方案数,要求给出这个构造。

解法一分析:由于 K 的范围很大,我们肯定不能 O(K) 建树,所以可以考虑什么样的树形结构可以只有1e5以内个节点就代表1e9种最优解。

画一画就能看出一个规律:在这里插入图片描述

这个图中的最小花费方案的花费是2,一共5个最小花费方案(2 * 2 + 1)

还不够明显?那我们再来画一个:
在这里插入图片描述
这个图有多少种最小花费方案呢?数一下发现一共是7种(2 * 3 + 1)。

也就是说,按照这样只有两条子链的构造,方案数等于左子链的个数乘以右子链的个数 ,再加上1(根节点)。
那么大概想一下以这种构造,如果要1e9种方案,会有多少个节点呢,两条链都是1e4.5个节点,也就是说最多会有2e4.5个节点,肯定小于1e5,所以可行。

然后你可能会发现,这样构造好像全是一个奇数,不可能出现偶数的情况啊,那如果方案数是偶数怎么办呢?实际上很简单,可以将根节点的权值变成 >2 的任何一个数,就可以将只选择根节点自身的最小花费方案删除,也就是进行了-1操作。比如方案数为4的时候,先将4开根号,得到2,说明每个子链需要有2个节点,然后4 - 2*2 = 0,说明已经不需要根节点了,就把根节点的权值改变。如下图:
在这里插入图片描述

然后我们再来找一个数字11,怎么来构造,首先为了保证节点数量尽可能小,我们需要对11取√,并且向下取整,得到3,说明每一条子链上都需要有3个节点,这是有9种方案,然后我们用11 - 9,剩余一个2,小于了3,发现没办法在子链上增加,那么我们就可以考虑增加根节点,相当于需要有两个根节点,如图:
在这里插入图片描述
代码很容易,直接模拟就可以。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define PLI pair<ll, ll>
#define PIL pair<int, ll>
#define endl '\n'
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1|1
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
#define debug(x) cerr << #x <<" = "<< x << endl
#define debug2(x, y) cerr << #x << " = " << x << " and " << #y << " = " << y << endl
#define Pi acos(-1)
using namespace std;

const int N = 1e5 + 100;

int a[N], fa[N];

int main() 
{

    int n;
    scanf("%d", &n);

    int xb;
    int num = (int)sqrt(n);
    int res = (n - num * num) % num;
    int add = (n - num * num) / num;

    if(res == 0)
        a[1] = 3;
    else
        for(int i = 1; i <= res; i ++)
            a[i] = 2, fa[i] = i - 1;
    
    if(res > 1) xb = res + 1;
    else res = 1;

    int l = res + 3, r = res + 2;
    
    a[res + 1] = a[res + 2] = 1, fa[res + 1] = fa[res + 2] = res;
    for(int j = 0; j < num; j ++, l += 2, r += 2)
        a[l] = a[r] = 1, fa[l] = l - 2, fa[r] = r - 2;

    r --;

    if(add != 0)
        for(int i = 1; i <= add; i ++, r ++)
            a[r] = 1, fa[r] = r - 1;

    printf("%d\n", res + 2 * num + add);
    for(int i = 1; i <= res + 2 * num + add - 1; i ++)
        printf("%d ", fa[i + 1]);
    puts("");
    for(int i = 1; i <= res + 2 * num + add; i ++)
        printf("%d ", a[i]);
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值