2016 ACM/ICPC亚洲区大连站-重现赛题解

The 2016 ACM-ICPC Asia Dalian Regional Contest [Cloned]

【A - Wrestling Match】

【题目大意】
有一个球队,队员有好坏之分,给你总人数n,m个对立关系,x个已知的好队员和y个已知的坏队员,问你能否将所有人分为两组,一组好一组坏

【解题思路】
二分图染色裸题

【AC代码】

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
const int maxm = 10010;
vector<int> G[maxn]; //存图
struct Edge {
	int from, to;
}edge[maxm]; //存边
int vis[maxn]; //记录颜色
bool flag = true; //记录答案
inline void init() { //初始化
	memset(vis, 0, sizeof(vis));
	flag = true;
	for (int i = 0; i <= 1000; ++i) {
		G[i].clear();
	}
}
inline void bfs(int s, int col) { //染色
	queue<int> que;
	que.push(s);
	vis[s] = col;
	while (!que.empty()) {
		int u = que.front();
		que.pop();
		for (int i = 0; i < G[u].size(); ++i) {
			int  v = G[u][i];
			if (vis[v] == vis[u]) {
				flag = false;
				return;
			}
			else if (!vis[v]) {
				vis[v] = -vis[u];
				que.push(v);
			}
		}
	}
}
int main() {
	int n, m, x, y;
	while (~scanf("%d%d%d%d", &n, &m, &x, &y)) {
		init();
		for (int i = 1; i <= m; ++i) {
			int u, v;
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
			edge[i].from = u;
			edge[i].to = v;
		}
		for (int i = 1; i <= x; ++i) {
			int temp;
			scanf("%d", &temp);
			if (vis[temp] == -1) { //好队员被染成坏的
				flag = false;
			}
			bfs(temp, 1);
		}
		for (int i = 1; i <= y; ++i) {
			int temp;
			scanf("%d", &temp); 
			if (vis[temp] == 1) { //坏队员被染成好的
				flag = false;
			}
			bfs(temp, -1);
		}
		if (!flag) {
			puts("NO");
			continue;
		}
		for (int i = 1; i <= m; ++i) {
			if (!vis[edge[i].from] && !vis[edge[i].to]) { //对还没有被染色的边的节点染色
				bfs(edge[i].from, 1);
			}
		}
		if (!flag) {
			puts("NO");
			continue;
		}
		for (int i = 1; i <= n; ++i) {
			if (!vis[i]) {  //有孤立节点
				flag = false;
				break;
			}
		}
		if (!flag) {
			puts("NO");
			continue;
		}
		puts("YES");
	}
	return 0;
}

【B - Regular Number】

【题目大意】
给你n行,每行m个数字,数字的行数为其优先级,输出母串中所有满足数字优先级的长度为n的字串

Sample Input
4
3 0 9 7
2 5 7
2 2 5
2 4 5
09755420524

Sample Output
9755
7554
0524

【解题思路】
使用bitset进行字符串匹配

设a[i][j]=1表示数字i可以出现在第j位上

b[i]=1表示当前0至i-1位已经符合优先级,即前i-1个数字已经匹配完成,现在判断当前数字是否能够作为第i位

那么问题来了,如何进行匹配

对母串从前往后遍历,首先我们优先考虑s[i]能否作为第一位,那么我们将b[0]置为1,让b = b&a[s[i]]如果b[0] == 1,那么s[i]就能够作为第0位,然后考虑s[i+1]能否作为第1位,由于b[i]表示前i-1位是否匹配,那么在找第1位的时候,我们可以让b左移1位,表示现在正在找第1位
0000001
0000010
当第0位匹配成功
假设前k-1位匹配成功
那么对于第k位,当前匹配到s[i],我们令b &= a[s[i]],如果b[k]等于1,说明匹配成功,b左移一位,进行第k+1位的匹配,否则b[k] = 0,说明匹配失败,重新从第0位开使匹配
当第n-1位匹配成功,那么该字串符合要求

需要注意,在匹配时,最重要的是第0位的匹配,我们在每一次匹配前都要将b[0]置为1,看s[i]能否最为第0位,这是匹配的前提

【AC代码】

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e6 + 10;
bitset<1010> a[11];
char s[maxn];
int main() {
	int n;
	while (~scanf("%d", &n)) {
		for (int i = 0; i <= 10; ++i) {
			a[i].reset();  //记得重置
		}
		for (int i = 0; i < n; ++i) {
			int m;
			scanf("%d", &m);
			for (int j = 1; j <= m; ++j) {
				int x;
				scanf("%d", &x);
				a[x][i] = 1;  //x在第i位上可用
			}
		}
		scanf(" %s", &s);
		bitset<1010> b;
		for (int i = 0; s[i] != '\0'; ++i) {
			b <<= 1;  //左移
			b[0] = 1;  //b[0]置1
			b &= a[s[i] - '0']; //匹配
			if (b[n - 1]) {  //n-1位匹配成功,输出字串
				char temp = s[i + 1];
				s[i + 1] = '\0';
				puts(s + i - n + 1);
				s[i + 1] = temp;
			}
		}
	}
	return 0;
}

【C - Game of Taking Stones】

【题目大意】
高精度威佐夫博弈

【解题思路】
Java大数~~(好麻烦的亚子,为什么16年没有python)~~

【AC代码】

import java.util.*;
import java.math.*;
class Main
{
	private static BigDecimal sqrt(BigDecimal x, int n)  //高精浮点数开根号(库里居然没有??)
	{
		BigDecimal ans = BigDecimal.ZERO;
		BigDecimal eps = BigDecimal.ONE;
		for (int i = 0; i < n; ++i)
		{
			while (ans.pow(2).compareTo(x) < 0)
			{
				ans = ans.add(eps);
			}
			ans = ans.subtract(eps);
			eps = eps.divide(BigDecimal.TEN);
		}
		return ans;
	};
	private static void Solve(BigDecimal a, BigDecimal b)  //威佐夫博弈,黄金分割比
	{
		BigDecimal c = sqrt(new BigDecimal(5), 120);
		c = c.add(BigDecimal.ONE).divide(new BigDecimal(2));
		if (b.subtract(a).multiply(c).setScale(0, BigDecimal.ROUND_DOWN).equals(a))
		{
			System.out.println(0);
		}
		else
			System.out.println(1);
	};
	public static void main(String[] args)
	{
		Scanner input = new Scanner(System.in);
		while (input.hasNext())
		{
			BigDecimal a = input.nextBigDecimal();
			BigDecimal b = input.nextBigDecimal();
			if (a.compareTo(b) == 1)
			{
				Solve(b, a);
			}
			else Solve(a, b);
		}
	};
};

【D - A Simple Math Problem】

【题目大意】
给定a和b,找到两个数x,y使得
1.x + y = a
2.lcm(x, y) = b

【解题思路】
设 x = cn,y = cm
则有
c * (n + m) = a
n * m * c = b
且 gcd(n, m) = 1
那么显然
gcd(n * m, n + m) = 1(可以用反证法推出x = y = 1)
故 gcd(a, b) = c = gcd(x, y)
那么得到方程组
x + y = a
x * y = b * gcd(a, b)
化简可以得到一个一元二次方程,根据判别式判断是否有解以及解是否为整数即可

【AC代码】

#include <bits/stdc++.h>
using namespace std;
inline int gcd(int x, int y) {
	if (y == 0) return x;
	return gcd(y, x % y);
}
int main() {
	int a, b;
	while (~scanf("%d%d", &a, &b)) {
		int m = a > b ? gcd(a, b) : gcd(b, a);
		int temp = a * a - 4 * m * b;
		if (temp < 0) {  //判别式小于零
			puts("No Solution");
		}
		else {
			int k = sqrt(temp);
			if (k * k != temp || (a - k) % 2 != 0) {  //解不为整数
				puts("No Solution");
			}
			else {
				int x = (a - k) / 2;
				int y = (a + k) / 2;
				printf("%d %d\n", x, y);
			}
		}
	}
	return 0;
}

【H - To begin or not to begin】

【题目大意】
盒子里有k个黑球和一个红球,问你先手和后手谁更有优势先摸到红球(摸到不放回)
For each case, output:
1, if the player who starts drawing has an advantage
2, if the player who starts drawing has a disadvantage
0, if Alice’s and Bob’s chances are equal, no matter who

【解题思路】
先手获胜的通项公式为:
P = [(k + 2) / 2] / (k + 1)

(具体推导参考:https://blog.csdn.net/qq_32265245/article/details/71074783)

若k为偶数
设 k = 2 * m
P = (m + 1) / (2 * m + 1)
1 - P = m / (2 * m + 1)
显然 P > 1 - P,先手优势,输出1

若k为奇数
设 k = 2 * m + 1
P = (m + 1) / (2 * m + 2) = 1 - P
先手与后手优势相同,输出0

【AC代码】

#include <bits/stdc++.h>
using namespace std;
int main() {
	int k;
	while (~scanf("%d", &k)) {
		if (k & 1) puts("0");
		else puts("1");
	}
	return 0;
}

【I - Convex】

【题目大意】
给定n个点,所有点到原点的距离为m,给定每个点与其相邻点与原点连线的角度,问你所有相邻点连起来的图形面积是多少

【解题思路】
将相邻两点与原点连成三角形,对Si = m * m * sin(angle) 求和

【AC代码】

#include <bits/stdc++.h>
using namespace std;
const double PI = acos(-1);  //acos(-1) = 3.1415926
int main() {
	int n, m;
	while (~scanf("%d%d", &n, &m)) {
		double angle, sum = 0;
		for (int i = 1; i <= n; ++i) {
			scanf("%lf", &angle);
			angle = angle / 180 * PI; //角度转弧度
			sum += sin(angle);  //对角度求和最后乘以距离的平方
		}
		double ans = sum * m * m * 0.5;
		printf("%.3f\n", ans);
	}
	return 0;
}

【J - Find Small A】

【题目大意】
这题题意理解起来着实有点麻烦,给你n个整数,每个整数二进制下为32位,而字符常量二进制下为,那么32位的整型每8位代表一个字符,问你所有32位整型拆分成4个8位后有几个字符‘a’(ACSII为97)

【解题思路】
对于每一个整数,将其拆分为4个8位,考虑整型在二进制下的0 - 8位如果等于97,就说明可以代表一个a,即对于整数x,( x % (1 << 8) ) == 97,为什么是(1 << 8),因为考虑的是0 - 8位所代表的十进制数,然后将x左移8位进行下一个字符的判断,直至x为0

【AC代码】

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n;
	scanf("%d", &n);
	int ans = 0;
	for (int i = 1; i <= n; ++i) {
		int x;
		scanf("%d", &x);
		while (x) {
			if (x % 256 == 97) { //(1 << 8) = 256
				++ans;
			}
			x >>= 8;
		}
	}
	printf("%d\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值