AtCoder Beginner Contest 199 (Sponsored by Panasonic) A~E 题解

A - Square Inequality

题目大意

给定三个整数$A,B,C$。判断$A^2+B^2 < C^2$是否成立。

$0\le A,B,C\le 1000$

输入格式

$A~B~C$

输出格式

如果$A^2+B^2 < C^2$,输出Yes;否则,输出No

样例

$A$ $B$ $C$ 输出
$2$ $2$ $4$ Yes
$10$ $10$ $10$ No
$3$ $4$ $5$ No

分析

直接按题意计算即可。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <cstdio>
using namespace std;

int main()
{
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	puts(a * a + b * b < c * c? "Yes": "No");
	return 0;
}

B - Intersection

题目大意

给定两个长度为$N$的序列:$A = (A_1, A_2, A_3, \dots, A_N)$和$B = (B_1, B_2, B_3, \dots, B_N)$。
找到符合如下条件的整数$x$的个数:

  • 对于所有的$1\le i\le N$,$A_i\le x\le B_i$。

$1\le N\le 100$
$1\le A_i\le B_i\le 1000$

输入格式

$N$
$A_1~A_2~\dots~A_N$
$B_1~B_2~\dots~B_N$

输出格式

输出答案。

样例

样例输入1

1
2
3
2
3 2
7 5

样例输出1

1
3

$x$可以取$3,4,5$。

样例输入2

1
2
3
3
1 5 3
10 7 3

样例输出2

1
0

没有$x$符合条件。

样例输入3

1
2
3
3
3 2 5
6 9 8

样例输出3

1
2

分析

我们将$x$的限制条件拆解为:

  • 对于所有的$1\le i\le N$,$A_i\le x$。
  • 对于所有的$1\le i\le N$,$x\le B_i$。

这时,我们可以进一步简化条件:

  • $(\max A)\le x$。
  • $x\le (\min B)$。

从而得到$(\max A)\le x\le (\min B)$,所以合法的$x$的个数为$\max(0,\min B-\max A+1)$。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <cstdio>
using namespace std;

int main()
{
	int n, maxa = 1, minb = 1000;
	scanf("%d", &n);
	for(int i=0; i<n; i++)
	{
		int a;
		scanf("%d", &a);
		if(a > maxa) maxa = a;
	}
	while(n--)
	{
		int b;
		scanf("%d", &b);
		if(b < minb) minb = b;
	}
	if(maxa > minb) puts("0");
	else printf("%d\n", minb - maxa + 1);
	return 0;
}

C - IPFL

题目大意

给定长度为$2N$且只由大写英文字母组成的字符串$S$。
你要处理$Q$个请求。
第$i$个请求中由三个整数$T_i,A_i$和$B_i$组成:

  • 如果$T_i=1$:交换$S$中第$A_i$和$B_i$个字符;
  • 如果$T_i=2$,交换$S$中的前$N$个和后$N$个字符(如:FLIP$\to$IPFL)。

输出执行所有请求后的$S$。

$1\le N\le 2\times 10^5$
$|S|=2N$
$1\le Q\le 3\times 10^5$
$1\le T_i\le 2$,如果$T_i=1$,$1\le A_i < B_i\le 2N$;如果$T_i=2$,$A_i=B_i=0$。

输入格式

$N$
$S$
$Q$
$T_1~A_1~B_1$
$T_2~A_2~B_2$
$\hspace{18pt}\vdots$
$T_Q~A_Q~B_Q$

样例

样例输入1

1
2
3
4
5
2
FLIP
2
2 0 0
1 1 4

样例输出1

1
LPFI

$\text{FLIP}\to\text{IPFL}\to\text{LPFI}$

样例输入2

1
2
3
4
5
6
7
8
9
2
FLIP
6
1 1 3
2 0 0
1 1 2
1 2 3
2 0 0
1 1 4

样例输出2

1
ILPF

分析

首先,$\mathcal O(NQ)$的模拟法肯定行不通,会TLE
我们考虑优化。
我们很容易发现,$T_i=1$的交换操作肯定是$\mathcal O(1)$的,但$T_i=2$的翻转操作是$\mathcal O(n)$的,所以需要优化。
我们可以用一个变量$\mathrm{flipped}$记录目前是否已经前后翻转($1$表示已经翻转,$0$表示没有翻转),这时,操作变为如下:

  • 当$T_i=2$,$\mathrm{flipped}:=1-\mathrm{flipped}$;
  • 当$T_i=1$且$\mathrm{flipped}=0$时,我们直接交换$A_i$和$B_i$
  • 当$T_i=\mathrm{flipped}=1$时,我们发现,一个位置$x$如果$< N$,则实际位置在$x+N$;否则,实际位置在$x-N$。

这种算法的时间复杂度为$\mathcal O(N+Q)$,可轻松通过此题。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <cstdio>
#define maxn 400005
using namespace std;

char s[maxn];
int n;

inline void swap(char& x, char& y) { x ^= y ^= x ^= y; }
inline char& calc(int pos) { return s[pos < n? pos + n: pos - n]; }

int main()
{
	scanf("%d%s", &n, s);
	int q;
	scanf("%d", &q);
	bool flipped = false;
	while(q--)
	{
		int t, a, b;
		scanf("%d%d%d", &t, &a, &b);
		a --, b --;
		if(t == 2) flipped = !flipped;
		else if(flipped) swap(calc(a), calc(b));
		else swap(s[a], s[b]);
	}
	if(flipped)
		for(int i=0; i<n; i++)
			swap(s[i], s[n + i]);
	puts(s);
	return 0;
}

D - RGB Coloring 2

题目大意

我们有一个有$N$个点和$M$条边的简单无向图,第$i$条边连接着顶点$A_i$和$B_i$。
我们要给这个图用三种不同的颜色着色,使得相邻的顶点有不同的颜色。
有多少种合法的着色方法?不一定要使用所有颜色。

$1\le N\le 20$
$0\le M\le \frac{N(N-1)}2$
$1\le A_i,B_i\le N$

输入格式

$N~M$
$A_1~B_1$
$A_2~B_2$
$\hspace{12pt}\vdots$
$A_M~B_M$

输出格式

输出答案。

样例

样例输入1

1
2
3
4
3 3
1 2
2 3
3 1

样例输出1

1
6

我们用RGB分别代表三种不同的颜色,则有$6$中不同的着色方法,它们分别是RGBRBGGRBGBRBRGBGR

样例输入2

1
3 0

样例输出2

1
27

这个图没有边,所以任意着色都是可行的,一共有$3^N=27$种方法。

样例输入3

1
2
3
4
5
6
7
4 6
1 2
2 3
3 4
2 4
1 3
1 4

样例输出3

1
0

这里没有合法方案。

样例输入4

1
20 0

样例输出4

1
3486784401

分析

我们将图中的每个连通块依次暴力算出所有可能的着色方案数,再相乘即可。
其实,这里我们最大的总尝试数不是$3^N$,而是$3\times 2^{N-1}$,因为使用$\text{DFS}$时每个点的前一个点的颜色已经定好了,只需要尝试两种可能即可。

代码

似乎没人发现可以用unsigned int吧……

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <cstdio>
#include <vector>
#define maxn 25
using namespace std;

vector<int> G[maxn];
int col[maxn], dep[maxn];

inline int next(int c) { return (c + 1) % 3; }

int paint(int v)
{
	for(int u: G[v])
		if(col[v] == col[u])
			return 0;
	int ans = 1;
	for(int u: G[v])
	{
		if(dep[u] == -1) dep[u] = dep[v] + 1;
		if(dep[u] == dep[v] + 1)
		{
			col[u] = next(col[v]);
			int res = paint(u);
			col[u] = next(col[u]);
			res += paint(u);
			col[u] = -1;
			if(res == 0) return 0;
			ans *= res;
		}
	}
	return ans;
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	while(m--)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		G[--x].push_back(--y);
		G[y].push_back(x);
	}
	for(int i=0; i<n; i++)
		col[i] = dep[i] = -1;
	unsigned int ans = 1;
	for(int i=0; i<n; i++)
		if(dep[i] == -1)
		{
			col[i] = dep[i] = 0;
			ans *= 3U * paint(i);
		}
	printf("%u\n", ans);
	return 0;
}

E - Permutation

题目大意

求符合如下条件的$(1,2,\dots,N)$的排列的个数:

  • 对于每个$1\le i\le M$,这个排列的前$X_i$个数中不超过$Y_i$的最多有$Z_i$个。

$2\le N\le 18$
$0\le M\le 100$
$1\le X_i,Y_i < N$
$0\le Z_i < N$

输入格式

$N~M$
$X_1~Y_1~Z_1$
$X_2~Y_2~Z_2$
$\hspace{18pt}\vdots$
$X_M~Y_M~Z_M$

输出格式

输出一个整数,即符合条件的排列的个数。

样例

样例输入1

1
2
3 1
2 2 1

样例输出1

1
4

四个符合条件的排列分别为:$(1,2,3)$、$(2,3,1)$、$(3,1,2)$、$(3,2,1)$。

样例输入2

1
2
3
5 2
3 3 2
4 4 3

样例输出2

1
90

样例输入3

1
18 0

样例输出3

1
6402373705728000

由于没有限制条件,所有的$18!=6402373705728000$个排列都可行。这也是本题的最大输出。

分析

首先,还是先排除$\mathcal O(N!\sum X)$的暴力算法,这样做的时间复杂度太高了。
我们考虑状压$\text{DP}$。令$\mathrm{dp}_\mathrm{mask}$表示从$(1,2,\dots,N)$中选出子序列$\mathrm{mask}$(二进制第$i$位是$0$表示不选$i$,$1$表示选$i$)。
则,$\mathrm{dp}_0=1$,动态转移方程为$\mathrm{dp}_\mathrm{mask}=\mathrm{mask}$中所有为的$1$位上把$1$变成$0$的$\mathrm{dp}$中的和,详见代码。
写代码时注意判断合法性,最终答案应为$\mathrm{dp}_{2^N-1}$(全选)。

代码

我这里做了一个小优化,即忽略$Z_i\ge Y_i$的条件。当然,我们也可以不用优化,但不能不用long long!!!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <cstdio>
#include <vector>
#define maxn 20
using namespace std;

using LL = long long;

vector<pair<int, int>> lim[maxn];
LL dp[1 << maxn];

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	while(m--)
	{
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		if(z < y) lim[x].emplace_back(y, z);
	}
	int mx = 1 << n;
	dp[0] = 1LL;
	for(int st=0; st<mx; st++)
	{
		vector<int> s;
		for(int i=0; i<n; i++)
			if(st >> i & 1)
				s.push_back(i);
		int cnt = __builtin_popcount(st);
		bool ok = true;
		for(auto [y, z]: lim[cnt])
		{
			int tot = 0;
			for(auto x: s)
				if(x < y && ++tot > z)
				{
					ok = false;
					break;
				}
			if(!ok) break;
		}
		if(ok) for(int x: s) dp[st] += dp[st ^ (1 << x)];
	}
	printf("%lld\n", dp[mx - 1]);
	return 0;
}
使用 Hugo 构建
主题 StackJimmy 设计