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;
}