Codeforces Codecraft-20(Div. 2) 题解

UPD:修正了笔误(之前把NTT写成了NNT)

这场比赛打得……真稀烂

A. Grade Allocation

原题链接
其实平均数不变的实质就是所有学生的分数总和不变。
由于每个学生的分数最少为0,所以我们只要把除了第1名学生之外的所有学生的分数全部给第1个学生就能使他的分数最大。
不过还有个限制条件:分数最大为m。
加上这个限制条件,代码就出来了。
见下:

#include<iostream>
using namespace std;
int a[1005];
int main()
{
	int t;
	cin >> t;
	while(t--)
	{
		int n, m;
		cin >> n >> m;
		int sum = 0;
		for(int i = 1; i <= n; i++)
		{
			cin >> a[i];
			if(i != 1)
				sum += a[i];
		}
		cout << min(m, sum + a[1]) << endl;	
	}
	return 0;
}

时间复杂度 O ( n ) O(n) O(n)

B. String Modification

原题链接
这道题我在参加比赛的时候AC的方法是强行找规律。
然后我按自己找到的规律写代码,就过了……
先把代码给出,读者可通过阅读代码体会我发现的规律。

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
struct res
{
	string str;
	int k;
};
res ans[5005];

bool cmp(res x, res y)
{
	if(x.str < y.str)
		return true;
	if(x.str > y.str)
		return false;
	return x.k < y.k;
}

int main()
{
	int t;
	cin >> t;
	while(t--)
	{
		int n;
		cin >> n;
		string s;
		cin >> s;
		for(int i = 1; i <= n; i++)
		{
			ans[i].k = i;
			string tmp = s;
			string st = tmp.substr(0, i - 1);
			if(i % 2 == n % 2)
				reverse(st.begin(), st.end());
			tmp += st;
			tmp.erase(0, i - 1);
			ans[i].str = tmp;
		}
		sort(ans + 1, ans + n + 1, cmp);
		cout << ans[1].str << endl << ans[1].k << endl;
	}
	return 0;
}

时间复杂度 O ( n 2 ) O(n^2) O(n2)

相信大家已经通过阅读程序明白了我发现的规律。
接下来我们来简略地证明一下:
首先我们有一个字符串 s s s
我们可以把它表示为 s 1 s 2 s 3 … s n s_1s_2s_3\dots s_n s1s2s3sn
然后我们选择一个 k k k

  • 经过第一次反转: s k s k − 1 s k − 2 … s 1 s k + 1 s k + 2 … s n s_ks_{k-1}s_{k-2}\dots s_1s_{k+1}s_{k+2}\dots s_n sksk1sk2s1sk+1sk+2sn
    观察到之后 s k s_k sk 的位置就不会发生变化了。
  • 经过第二次反转: s k s k + 1 s 1 … s k − 2 s k − 1 s k + 2 … s n s_ks_{k+1}s_1\dots s_{k-2}s_{k-1}s_{k+2}\dots s_n sksk+1s1sk2sk1sk+2sn
    观察到之后 s k s k + 1 s_ks_{k+1} sksk+1 的位置都不会发生变化。
  • 经过第三次反转: s k s k + 1 s k + 2 … s 2 s 1 s k + 3 … s n s_ks_{k+1}s_{k+2}\dots s_2s_1s_{k+3}\dots s_n sksk+1sk+2s2s1sk+3sn]
    观察到之后 s k s k + 1 s k + 2 s_ks_{k+1}s_{k+2} sksk+1sk+2 的位置不会再发生变化。

……
以此类推,最后反转变换过后的字符串 s ′ s' s n − k + 1 n-k+1 nk+1 个字符一定是 s k s k + 1 s k + 2 … s n s_ks_{k+1}s_{k+2}\dots s_n sksk+1sk+2sn
通过观察还可发现, s s s 中的前 k − 1 k-1 k1 个字符 s 1 s 2 s 3 … s k − 1 s_1s_2s_3\dots s_{k-1} s1s2s3sk1 一定会被搬到 s ′ s' s 的后半部分,但是方向不确定。
实际上,不难得到,若反转次数为奇数,则最后 s ′ s' s 的后半部分为 s k − 1 s k − 2 s k − 3 … s 1 s_{k-1}s_{k-2}s_{k-3}\dots s_1 sk1sk2sk3s1 ;反之,若反转次数为偶数,则最后 s ′ s' s 的后半部分为 s 1 s 2 s 3 … s k − 1 s_1s_2s_3\dots s_{k-1} s1s2s3sk1
我们的反转次数是 n − k + 1 n-k+1 nk+1 ,所以当且仅当 n n n k k k 奇偶性相同时反转次数为奇数。
于是代码就不难理解了。

C. Primitive Primes

原题链接

这道毒瘤题,卡了我1h30min还没做出来。啊啊啊啊……
其实刚开始我的思路是算出所有的 c c c 再枚举 t t t
于是想到NTT。(实际上是我的同学想到的)
然后TLE了。
我又想找到计算系数的规律并优化。
然而还是失败了……
(肯定不是NTT,Div.2 的C题怎么可能是省选难度……)
接下来我开始思考:

  1. 为什么题目要规定 p p p 是质数?
  2. 为什么题目中有如下两个条件?
    g c d ( a 0 , a 1 , a 2 … a n − 1 ) = 1 gcd(a_0,a_1,a_2\dots a_{n-1})=1 gcd(a0,a1,a2an1)=1
    g c d ( b 0 , b 1 , b 2 … b m − 1 ) = 1 gcd(b_0,b_1,b_2\dots b_{m-1})=1 gcd(b0,b1,b2bm1)=1

然后我开始思考 p p p 是质数能得到什么,接着我就想出了正解——
首先,
c i = a 0 ⋅ b i + a 1 ⋅ b i − 1 + a 2 ⋅ b i − 2 + ⋯ + a i − 2 ⋅ b 2 + a i − 1 ⋅ b 1 + a i ⋅ b 0 c_i = a_0\cdot b_i+a_1\cdot b_{i-1}+a_2\cdot b_{i-2}+\dots +a_{i-2}\cdot b_2+a_{i-1}\cdot b_1+a_i\cdot b_0 ci=a0bi+a1bi1+a2bi2++ai2b2+ai1b1+aib0 .
要让 c i   m o d   p ≠ 0 c_i\ mod\ p \neq 0 ci mod p=0,则上方多项式的每一项中,至少有一项中的 a a a b b b 满足 a   m o d   p ≠ 0 a\ mod\ p \neq 0 a mod p=0 b   m o d   p ≠ 0 b \ mod\ p \neq 0 b mod p=0
所以我们在 a a a 数组中找到第一个 a x   m o d   p ≠ 0 a_x\ mod\ p\neq 0 ax mod p=0 ,在 b b b 数组中找到第一个 b y   m o d   p ≠ 0 b_y\ mod\ p\neq 0 by mod p=0 ,满足条件的 t t t 就等于 x + y x+y x+y

那么又有一个问题—— g c d ( a 0 , a 1 , a 2 … a n − 1 ) = g c d ( b 0 , b 1 , b 2 … b m − 1 ) = 1 gcd(a_0,a_1,a_2\dots a_{n-1})=gcd(b_0,b_1,b_2\dots b_{m-1})=1 gcd(a0,a1,a2an1)=gcd(b0,b1,b2bm1)=1 有什么用?
这句话向我们保证了不可能对于所有的 i ∈ [ 0 , n − 1 ] i\in [0,n-1] i[0,n1] , a i   m o d   p = 0 a_i\ mod\ p=0 ai mod p=0
以及不可能对于所有的 i ∈ [ 0 , m − 1 ] i\in [0,m-1] i[0,m1] , b i   m o d   p = 0 b_i\ mod\ p=0 bi mod p=0
简单来说——题目保证有解。
(不过题目已经说了一定存在符合条件的 t t t

AC代码如下:(真的很简单)

#include<cstdio>
const int maxx = 1000010;
int a[maxx], b[maxx];

inline int read()
{
    int res = 0;
    char ch = getchar();
    bool f = true;
    for (; ch < '0' || ch > '9'; ch = getchar())
        if (ch == '-')
            f = false;
    for (; ch >= '0' && ch <= '9'; ch = getchar())
		res = (res << 1) + (res << 3) + (ch ^ 48);
    return f ? res : -res;
}

int main()
{
	int n, m, p;
	n = read();
	m = read();
	p = read();
	for(int i = 0; i < n; i++)
		a[i] = read();
	for(int i = 0; i < m; i++)
		b[i] = read();
	int x = 0;
	while(a[x] % p == 0)
		x++;
	int y = 0;
	while(b[y] % p == 0)
		y++;
	printf("%d\n",x+y);
	return 0;
}

时间复杂度 O ( n + m ) O(n+m) O(n+m)

(这题做的不好的原因是思考方向从一开始就错了。我们应该算出一个符合条件的 t t t 而不是算出所有的 c c c
这道题我直到比赛结束都没有交上去呜呜呜呜~ 〒▽〒

D. Nash Matrix

原题链接
其实我们可以把所有的点分成两类:停不下来的和会停下来的。
我们优先解决会停下来的点。

Case 1:

首先,如果一个点 ( i , j ) (i,j) (i,j) 的终点为 ( i , j ) (i,j) (i,j) ,那么毫无疑问,这个点肯定会被标记成 X X X
然后我们从这个点出发dfs(当然bfs也可以),找到所有终点同样是 ( i , j ) (i,j) (i,j) 的点,然后在路径上标记对应的字符。这样我们就把所有终点是同一点的格点连接在了一起。
所有停下来的点就解决了。

Case 2:

接下来我们来解决不会停下来的点。
如果从一个点出发不会停下来,那么只有两种情况:

  1. 这个点在一个环内;
  2. 这个点有一条路径到达一个环。

而建立环的最简单的办法就是让两个点互相可达。
所以我们把所有能配对的相邻的两个点配对,再把剩下的点尽可能往其中一个环上添加路径。
解决!

How to judge the result?

如果在建环配对时有一个点不能配对,那么结果就是INVALID
如果有任何一个格点上没有标记路径,那么结果就是INVALID
反之结果就是VALID

Code

附上AC代码:

#include<cstdio> 
const int maxn = 1005;
int n;
char matrix[maxn][maxn];
int x[maxn][maxn], y[maxn][maxn];

void dfs(int p, int q, char c)
{
	if(matrix[p][q] != '\0')
		return;
	matrix[p][q] = c;
	if(x[p - 1][q] == x[p][q] && y[p - 1][q] == y[p][q])//up
		dfs(p - 1, q, 'D');
	if(x[p + 1][q] == x[p][q] && y[p + 1][q] == y[p][q])//down
		dfs(p + 1, q, 'U');
	if(x[p][q - 1] == x[p][q] && y[p][q - 1] == y[p][q])//left
		dfs(p, q - 1, 'R');
	if(x[p][q + 1] == x[p][q] && y[p][q + 1] == y[p][q])//right
		dfs(p, q + 1, 'L');
}

bool connect(int p,int q,int r,int s,char c1,char c2)
{
	if(x[r][s] == -1 && y[r][s] == -1)
	{
		matrix[p][q] = c1;
		if(matrix[r][s] == '\0')
			matrix[r][s] = c2;
		return true;
	}
	else
		return false;
}

int main()
{
	scanf("%d",&n);
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			scanf("%d%d",&x[i][j],&y[i][j]);
	//Case 1
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			if(x[i][j] == i && y[i][j] == j)
				dfs(i, j, 'X');
	//Case 2
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			if(x[i][j] == -1 && y[i][j] == -1)
			{
				bool flag = (matrix[i][j] != '\0');
				if(!flag)//from left to right
					flag = connect(i,j,i,j+1,'R','L');
				if(!flag)//from right to left
					flag = connect(i,j,i,j-1,'L','R');
				if(!flag)//from down to up
					flag = connect(i,j,i-1,j,'U','D');
				if(!flag)//from up to down
					flag = connect(i,j,i+1,j,'D','U');
				if(!flag)
				{
					printf("INVALID\n");
					return 0;
				}
			}
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			if(matrix[i][j] == '\0')
			{
				printf("INVALID\n");
				return 0;
			}
	printf("VALID\n");
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
			printf("%c",matrix[i][j]);
		printf("\n");
	}
	return 0;
}

时间复杂度 O ( n 2 ) O(n^2) O(n2)

特别说明

由于本人能力有限,题解只写到D,还请见谅 😃

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值