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
s1s2s3…sn 。
然后我们选择一个
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
sksk−1sk−2…s1sk+1sk+2…sn
观察到之后 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+1s1…sk−2sk−1sk+2…sn
观察到之后 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+2…s2s1sk+3…sn]
观察到之后 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
n−k+1 个字符一定是
s
k
s
k
+
1
s
k
+
2
…
s
n
s_ks_{k+1}s_{k+2}\dots s_n
sksk+1sk+2…sn 。
通过观察还可发现,
s
s
s 中的前
k
−
1
k-1
k−1 个字符
s
1
s
2
s
3
…
s
k
−
1
s_1s_2s_3\dots s_{k-1}
s1s2s3…sk−1 一定会被搬到
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
sk−1sk−2sk−3…s1 ;反之,若反转次数为偶数,则最后
s
′
s'
s′ 的后半部分为
s
1
s
2
s
3
…
s
k
−
1
s_1s_2s_3\dots s_{k-1}
s1s2s3…sk−1 。
我们的反转次数是
n
−
k
+
1
n-k+1
n−k+1 ,所以当且仅当
n
n
n 与
k
k
k 奇偶性相同时反转次数为奇数。
于是代码就不难理解了。
C. Primitive Primes
这道毒瘤题,卡了我1h30min还没做出来。啊啊啊啊……
其实刚开始我的思路是算出所有的
c
c
c 再枚举
t
t
t 。
于是想到NTT。(实际上是我的同学想到的)
然后TLE了。
我又想找到计算系数的规律并优化。
然而还是失败了……
(肯定不是NTT,Div.2 的C题怎么可能是省选难度……)
接下来我开始思考:
- 为什么题目要规定 p p p 是质数?
- 为什么题目中有如下两个条件?
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,a2…an−1)=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,b2…bm−1)=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=a0⋅bi+a1⋅bi−1+a2⋅bi−2+⋯+ai−2⋅b2+ai−1⋅b1+ai⋅b0 .
要让
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,a2…an−1)=gcd(b0,b1,b2…bm−1)=1 有什么用?
这句话向我们保证了不可能对于所有的
i
∈
[
0
,
n
−
1
]
i\in [0,n-1]
i∈[0,n−1] ,
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,m−1] ,
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:
接下来我们来解决不会停下来的点。
如果从一个点出发不会停下来,那么只有两种情况:
- 这个点在一个环内;
- 这个点有一条路径到达一个环。
而建立环的最简单的办法就是让两个点互相可达。
所以我们把所有能配对的相邻的两个点配对,再把剩下的点尽可能往其中一个环上添加路径。
解决!
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,还请见谅 😃