2019 香港区域赛 BDEG 题解

B.Binary Tree

题意:给你一棵二叉树。有两个游戏者,回合制,他们每次可以删去这棵二叉树中的一棵满二叉树。求最后谁赢。

解法:每一棵满二叉树有奇数个节点,那么每次游戏者只能删去奇数个节点,所以直接通过给定的二叉树节点的奇偶就可以判断是谁赢了。

#include<cstdio>
#include<cstring>
using namespace std;

int main() {
	int T; scanf("%d",&T);
	while(T--) {
		int n; scanf("%d",&n);
		for(int i=0;i<n-1;i++) {
			int u,v; scanf("%d%d",&u,&v);
		}
		if(n&1) printf("Alice
");
		else printf("Bob
");
	}
	return 0;
}
D.Defining Labels

题意:大模拟

做法:大模拟

附队友代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<algorithm>
#define max(a,b)   (a>b?a:b)
#define min(a,b)   (a<b?a:b)
#define swap(a,b)  (a=a+b,b=a-b,a=a-b)
#define memset(x,y) memset(x,y,sizeof(x))
#define ll long long
using namespace std;
int t;
int k,x,a[15],ans[100];
int main() {
	cin>>t;
	while(t--) {
		cin>>k>>x;
		int tot=0;
		a[1]=10-k;
		for(int i=2; i<=k; i++)a[i]=a[i-1]+1;
		while(x) {
			ans[++tot]=a[(x-1)%k+1];
			if(x%k==0) x/=k,x--;
			else x/=k;
		}
		while(tot--) cout<<ans[tot+1];
		cout<<endl;
	}
}

E.Erasing Numbers

题意:给你一个长度为奇数排列,你每次可以选这个排列中连续的三个数,删去这三个数中最大值和最小值。这样操作直到排列只剩下一个数。你需要求出排列中每个数是否有可能可以被保留到最后。

做法:枚举每一个数是否能被留下。我们需要把大于他的数和小于他的数删到个数相等,这样这个数一定可以留下来。假如这个数小于 (frac{n+1}{2}) ,那我们就删连续三个大于它的数,反过来就删连续三个小于它的数。

假设现在这个数小于 (frac{n+1}{2}) ,当前是大大小这样排,相当于一个大,而大小这样排,相当于抵消了。最后判断一下最大可以删的次数和到平衡时应该删的次数大小关系即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define IL inline
#define ri register int

const int N = 5000 + 5;

int n;
int a[N];

int del(int l,int r,int pos,bool flag) {
	int ret = 0,now = 0;
	for(int i=l;i<=r;i++) {
		if(!flag) {
			if(a[i] > a[pos]) {
				if(++now == 3) now = 1, ret++;
			}
			else if(--now == -1) now = 0;
		}
		else {
			if(a[i] < a[pos]) {
				if(++now == 3) now = 1, ret++;
			}
			else if(--now == -1) now = 0;
		}
	}
	return ret;
}

int main() {
	int T; scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		for(int i=0;i<n;i++) scanf("%d",&a[i]);
		for(int i=0;i<n;i++) {
			bool flag = false;
			if( a[i] >= (n+1)/2 ) flag = true;
			printf("%d",del(0,i-1,i,flag)+del(i+1,n-1,i,flag) >= abs((n+1)/2 - a[i]));
		}
		printf("
");
	}
	return 0;
}
G.Game Design

题意:你需要构造一棵带点权的树,使得阻断这棵树从叶子节点到根的最小花费方案数为 K。

做法:第一反应构造二叉树,因为多叉树不好维护。然后想到二进制分解。假设有一棵树,我们让左子树为一直维护的东西,右子树用来乘 2(这里直接用两个点权都为 1 的点连成链即可)。然后就是如何控制每一位是 1 还是 0 了。假设 (ans_i) 数组为以 i 为根的子树切断它的最小花费,(c_i) 数组为每个点的点权,显然有

[ans_i = egin{cases}c_i,c_i < ans_{lch} + 1\c_i+1,c_i = ans_{lch} + 1\ans_{lch}+1,c_i > ans_{lch} + 1end{cases} ]

假设方案数数组为 (d_i) ,那么

[d_i = egin{cases}1,c_i < ans_{lch} + 1\2 cdot d_{lch} + 1,c_i = ans_{lch} + 1\2 cdot d_{lch},c_i > ans_{lch} + 1end{cases} ]

这样利用后两行,就可以二进制分解了。

我的代码很混乱(模拟的时候瞎写)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define IL inline 

const int N = 1e5 + 3;

int KK;
int cnt;
int a[N],pa[N],ans[N];

void build() {
	int highbit;
	for(int i=31;i>=0;i--) {
		if(KK&(1<<i)) {
			highbit = i;
			break;
		}
	}
	
	cnt++;
	a[cnt] = ans[cnt] = 1;
	
	for(int i=highbit-1;i>=0;i--) {
		if(KK&(1<<i)) {
			a[cnt+3] = ans[cnt] + 1;
		}
		else {
			a[cnt+3] = ans[cnt] + 2;
		}
		ans[cnt+3] = ans[cnt] + 1;
		a[cnt+1] = a[cnt+2] = 1;
		pa[cnt] = cnt + 3;
		pa[cnt+1] = cnt + 3;
		pa[cnt+2] = cnt + 1;
		cnt += 3;
	}
	pa[cnt] = 1;
	a[1] = 1e9;
}

int main() {
	scanf("%d",&KK);
	cnt = 1; fill(a,a+N,0); fill(pa,pa+N,0); fill(ans,ans+N,0);
	build();
	printf("%d
",cnt);
	for(int i=2;i<cnt;i++) printf("%d ",pa[i]);
	printf("%d
",pa[cnt]);
	for(int i=1;i<cnt;i++) printf("%d ",a[i]);
	printf("%d
",a[cnt]);
	return 0;
}

参考blog

https://blog.csdn.net/weixin_40524635/article/details/103580724

原文地址:https://www.cnblogs.com/bringlu/p/12635155.html