SDNU ACM-ICPC 2018 Final Summary Contest(12-16)【解题报告】

前言:

激动人心(×)的期末总结赛终于来了!经过断断续续的三周的准备,这套题目终于完整(×)的出现在了大家面前。虽然此次13道题全是英文题面,但命题组尽可能的保证了题目的通俗易懂……如果还是有读不懂的地方的话就来喷我吧,我太菜了o(╥﹏╥)o。

另外,感觉到了造数据的不易,找的两个验题人一直忙着各种事咕咕咕,终于在昨晚把他们骗到集训室帮忙验了下题,改了些题面描述的语法错误,果然有道题数据出问题了。原来自己随机程序写的有问题,emmm。抓紧修锅,最终赶在封楼前修完,安心回宿舍了(要是今天现场出锅就刺激了)

UPD:MD,今天果然还是出锅了。

题解:

说明:

以下内容为现场赛时写的,一边看榜,一边写,所以是按照每道题被拿走FB的顺序写的。

1231.Why choose ACM?【签到】

最后加的签到题,没想到00:03:09被QLU的抢了一血(By:QLUZhouXiang),以为这个签到题伪装的挺好的了。

另外,感觉自己已经描述的够详细了,所以WA了的…emmm,求轻喷吧。

MyCode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

int main()
{
string s = "always challenge miracles";
s[0] = toupper(s[0]);
cout << s << '\n';
return 0;
}

1536.How many users on SDNU OJ【签到】【stl】

一血时间00:05:30,By:songjian。肯定是发现了这个题题面的提示(签到成功.jpg)。

也没啥好说的,就是直接map|set标记,然后输出就好了。

另外:输出Case这样的比较恶心的&无用的东西的时候,直接按照下面的写法比较方便,可以学习一下。

MyCode:

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
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;

int t, n;
string s;
map<string, bool> MP;
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d", &t);
for(int cas = 1; cas <= t; ++cas)
{
scanf("%d", &n);
MP.clear();
while(n--)
{
cin >> s;
MP[s] = 1;
}
printf("Case #%d: %d\n", cas, MP.size());
}
return 0;
}

1534.Single Dog【签到】

签到题,再次被QLU参赛选手发现,00:19:38FB诞生,By: QLUZhouXiang

这个恶意卡了一下int,n的最大范围是INT_MAX + 1(果然卡了很多人),其他没什么好说的。

MyCode:

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

int t;
long long n;
char name[111];
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d", &t);
for(int cas = 1; cas <= t; ++cas)
{
scanf("%s%lld", name, &n);
printf("Case #%d: %s is %s.\n", cas, name, (n >= 10000) ? "Life Winner" : "Single Dog");
}
return 0;
}

1541.Your Code Is Awesome【二进制异或】【思维】

00:33:42FB诞生,By:18XiWenjuan

看到她AC后,心里一阵窃喜,原来我讲过的异或的性质她认真学了啊,打开AC代码一看,笑容渐渐消失。

WTF?排了个序,然后相邻两个查了一下,过了?本着赛中不能把AC过的进行重判的原则,就勉强让她AC吧。But,这不是正解,这次卡过去了就卡过去吧,连夜修锅最后还是输了。赛后再加强数据嘻嘻。

???怎么都知道用排序水过去?不行不行,忍不了,现场加强数据,rejudge,尽情的喷我吧,告辞。

UPD:03:06:02这道题又被卡过去了,打开AC代码看了一下,原来是加上了读入挂,心态爆炸。不想再改了,我二十几岁,我好累。对于卡过去的柳总,如果你看到这里,我要送你下面这句话:

AC代码:

参考https://blog.csdn.net/ccutyear/article/details/53456571

1537.Tiger Eat People【组合数学】【大数或模拟】

00:34:38FB诞生,By:2017luoxingcheng

没想到这道题在所有签到题没签完的前提下被秒了,这样很容易歪榜啊喂(#`O′)(其实如果不是卡高精度了这题也是道水题)。

解析:这道题相当于6进制下选数,很容易看出答案就是$2^n(C_n^0 + C_n^1 + C_n^2 + \ldots + C_n^n)$,因为最后答案比较大,所以可以用c++模拟或者直接上Java(还有人用的Python?tql)。

MyCode(Java):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.math.BigInteger;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int t = cin.nextInt();
BigInteger TWO = BigInteger.valueOf(2);
while((t--) > 0)
{
int n = cin.nextInt();
BigInteger res = BigInteger.ONE;
for(int i = 0; i < n; ++i)
{
res = res.multiply(TWO);
}
System.out.println(res);
}
}
}

1539.Do you like Hot Dog ?【01背包】【W超大】

00:38:37 QLU抢走一血,By:QLUzhengzehao

就是01背包问题,小白书上写的很详细,这个价值范围小,所以用DP针对不同的价值计算最小重量。详见P61。

MyCode:

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
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 10010;
const int INF = 0x3f3f3f3f;
int n, limit;
int we[500 + 5];
int vi[500 + 5];
int dp[505][5005];
void solve()
{
fill(dp[0], dp[0] + 5005, INF);
dp[0][0] = 0;
for(int i = 0; i < n; ++i)
for(int j = 0; j <= 5001; ++j)
{
if(j < vi[i])
dp[i+1][j] = dp[i][j];
else
dp[i+1][j] = min(dp[i][j], dp[i][j-vi[i]]+we[i]);
}
int res = 0;
for(int i = 0; i <= 5001; ++i)
if(dp[n][i] <= limit)
res = i;
printf("%d\n",res);
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int nCase;
scanf("%d", &nCase);
while(nCase--)
{
memset(we, 0, sizeof we);
memset(vi, 0, sizeof vi);
// memset(dp, 0, sizeof dp);
scanf("%d %d", &n, &limit);
for(int q = 0; q < n; ++q)
{
scanf("%d %d", &we[q], &vi[q]);
}
solve();
// cout << "limit=" << limit <<endl;
//cout << rec(0, limit) << endl;
}
return 0;
}

1331.Kick Veges’ Ass【二分】

01:55:50FB诞生,By:2017luoxingcheng

题意:n个菜鸟站一排,我要按顺序虐他们。虐第i个菜鸟需要花费掉A[i]点RP,现在我打算分k天虐完这些菜鸟。我每天的RP总数是固定的,为了使RP最低的时候不会过低导致杯具,我希望这k天中虐菜花费RP最多的一天,花费的RP尽量少。求我在花费RP最多那天花费了多少RP。

思路:二分枚举答案就好了,最基础的二分。

MyCode:(一年半前写的,比较丑陋,不要参考)

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
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAX = 100005;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;

int n, k;
int l, r, mid, sum;
int a[MAX];
bool judge(int rp)
{
int tot = 1; //全部虐完需要的天数
int now = 0; //现在已经花费的RP
for(int i = 0; i < n; ++i)
{
now += a[i];
if(now > rp)
{
now = a[i];
++tot;
}
}
// cout << "tot=" << tot << endl;
return tot > k;
}
int main()
{
cin >> n >> k;
for(int i = 0; i < n; ++i)
{
cin >> a[i];
l = max(l, a[i]);
r += a[i];
}
while(l < r)
{
int mid = (l + r) / 2;
if(judge(mid))
l = mid + 1;
else
r = mid;
// cout << l << "--" << r << "--" << mid << endl;
}
cout << r <<endl;
return 0;
}

1220.Look for homework【BFS记录路径】

01:56:02FB诞生,By:2018lihaoran

BFS + 记录路径。确实还是没啥好说的。。。

MyCode(两年前的,现在看起来很丑陋,不要参考):

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n, m;
int fir, las;
int dir[4][2] = {{1,0},{0,-1},{0, 1},{-1, 0} };//D,L,R,U
char T[4] = {'D', 'L', 'R', 'U'};
int mapa[15][15];
string str[105];
char c[105];

struct node
{
int x, y;
int pre;
char ch;
} q[105];

void print(int i, int j)
{
int tem, tot = 0;
c[tot] = q[j].ch;
tot++;
tem = i;
while(q[tem].pre != -1)
{
c[tot] = q[tem].ch;
tot++;
tem = q[tem].pre;
}
cout << tot << endl;
for(tem = tot-1; tem >= 0; --tem)
cout << c[tem];
cout << endl;
}

void bfs(int x, int y)
{
int x1, y1;
fir = 0;
las = 1;
q[fir].x = x;
q[fir].y = y;
q[fir].pre = -1;
mapa[x][y] = 1;
while(fir < las)
{
for(int i = 0; i < 4; ++i)
{
x1 = q[fir].x + dir[i][0];
y1 = q[fir].y + dir[i][1];
if(x1 < 0 || x1 >= n || y1 < 0 || y1 >= m || mapa[x1][y1])
continue;

mapa[x1][y1] = 1;
q[las].pre = fir;
q[las].x = x1;
q[las].y = y1;
q[las].ch = T[i];
las++;

if(x1 == n-1 && y1 == m-1)
{
print(fir, las-1);
return ;
}
}
fir++;
}
}

int main()
{
while(~scanf("%d%d",&n, &m))
{
for(int i = 0; i < n; ++i)
{
cin>>str[i];
for(int j=0; j<=m-1; j++)
{
if(str[i][j]=='1')
mapa[i][j]=1;
else
mapa[i][j]=0;
}
}
bfs(0, 0);
}
return 0;
}

1542.Liu Yuxin was insulted【状压DP】

02:16:51 FB诞生,By:18LiuJunxiang

出题人说是状压DP,但是状态太少,直接枚举也能过,看了一下柳总的AC代码,近3kb,真猛啊。。

ACCode(By LiuYuxin):

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
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int m[1005][10], n;
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
memset(m, inf, sizeof m);
scanf("%d", &n);
m[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 8; j++)
m[i][j] = m[i - 1][j];
int v, kind = 0, len;
char r[100];
scanf("%d", &v);scanf("%s", r);
len = strlen(r);
int now = 0;
while (now < len) {
if (r[now] == 'O') {
kind |= (1 << 0);
now += 4;
}
else if (r[now] == 'G') {
kind |= (1 << 1);
now += 9;
}
else if (r[now] == 'P') {
kind |= (1 << 2);
now += 10;
}
}
for (int j = 0; j < 8; j++) {
int t = (kind | j);
m[i][t] = min(m[i][t], m[i - 1][j] + v);
}
}
if (m[n][7] < inf)
printf("%d\n", m[n][7]);
else
cout << "Where are you? My beloved." << endl;
return 0;
}

1543.Happy Salted Fish Every Day【签到】【模拟】

02:36:19 FB诞生,By:2017liruoshui

2个半小时了,签到题终于全被签完了,不知道是不是我暗示了一下的原因(我说其实还有好多题可以做的)。。另外,不明白为啥昌写了1600+b代码而且还WA了。

题意:告诉你一个序列的生成方式,问这个序列的第n项是几。

思路:直接模拟,还原序列后,$O(1)$输出。

ACCode(From 多校std):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//17多校 第七场K题
#include <cstdio>
int k[10000005],a,i,j,ii,T,n;
int main(){
k[1]=a=1;k[2]=k[3]=2;i=3;j=4;
while(j<=10000000){
for(ii=1;ii<=k[i];ii++) k[j++]=a;
i++;a=3-a;
}
scanf("%d",&T);
while(T--){
scanf("%d",&n);
printf("%d\n",k[n]);
}
}

1535.Math【质因数分解】【贪心】

Codeforces Round #520 (Div. 2) B题。

这都做不出来,我真是无话可说。。可能是被题面里出现的YC给吓到了?其实还是菜。

题意:给你一个数$n$,要你输出经过若干次操作能得到最小的值和操作的最少次数。

一共有两种操作:1. 让$n = n \times x$,$x$必须是正数。 2. 让$n = \sqrt{n}$,$n$必须是个平方数。

思路:最后能得到的最小的数一定是将$n$进行质因数分解后各质因子的乘积(通过操作2使他们的幂次都降为1)。为了使操作次数最小,我们可以一次乘上个很大的数,使得所有质因子都可以不断进行开方操作直到开到幂次为1,而能开直接到1的前提是让幂次变为$2^n$,具体细节见代码吧。

MyCode:

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 111;

int n, p[N], num[N];
int main()
{
while(~scanf("%d", &n))
{

if(n == 1)
{
puts("1 0");
continue;
}

memset(p, 0, sizeof(p));
memset(num, 0, sizeof(num));
int cou = 0;
for(int i = 2; i <= sqrt(n); ++i)
{
if(n % i == 0)
{
++cou;
p[cou] = i;
num[cou] = 0;
while(n % i ==0)
{
num[cou]++;
n /= i;
}
}
}
if(n > 1)
{
p[++cou] = n;
num[cou] = 1;
}

int maxx = num[1], res1 = p[1], res2 = 0;
for(int i = 2; i <= cou; ++i)
{
maxx = max(maxx, num[i]);
res1 = res1 * p[i];
}

if(maxx == 1)
res2 = 0;
else
{
bool f = 0;
for(int i = 2, j = 1; ; i *= 2, ++j)
{
if(maxx <= i)
{
res2 = j;
if(maxx < i)
{
f = 1;
++res2;
}
break;
}
}
// cout << maxx << ' ' << res2 << '\n';
if(!f)
for(int i = 1; i <= cou; ++i)
if(num[i] != maxx)
{
++res2;
break;
}
}

printf("%d %d\n", res1, res2);
}
return 0;
}

1538.GTMDDLY【贪心】【模拟】

ACCode(出题人提供):

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//Created by SWT
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int x,n;
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
while(~scanf("%d %d",&x,&n))
{
int part1 = n*3,my_lose = 0,her_lose = 0;
her_lose += (1 + 2*part1) * part1 - 3*part1;
my_lose += (1 + part1) * part1 / 2;
my_lose += 3*(1 + n) * n / 2;
int flag = 0;
if(x > max(my_lose,her_lose))
{
int my_hurt = part1 + 1;
int her_hurt = 2 * part1 - 1;
while (1)
{
my_lose += my_hurt;
if(my_lose >= x)
{
flag = -1;
break;
}
her_lose += her_hurt;
if(her_lose >= x)
{
flag = 1;
break;
}
my_hurt++;
her_hurt++;
}
}
else {
int attack = n,her_hurt = 1;
my_lose = her_lose = 0;
for (int i = 1; i <= 3*n; i++)
{
if(i != 1 && i % 3 == 0)attack--;
my_lose += i;
if(my_lose >= x)
{
flag = -1;break;
}
her_lose += her_hurt;
her_hurt++;
if(her_lose >= x)
{
flag = 1;break;
}
her_lose += her_hurt;
her_hurt++;
if(her_lose >= x)
{
flag = 1;break;
}
my_lose += attack;
her_lose -= 3;
if(my_lose >= x)
{
flag = -1;break;
}
}
}
if(flag > 0)printf("skirt!\n");
else printf("Damn it!\n");

}
}

1540.A list generated by a wrong list【思维+NNT+二分】【防AK】

0提交,太真实了。

以下内容由出题人(Forsaken)提供:

考察范围:

欧拉筛的深入理解、二分、NTT

题意:

题目中告知我们运行的欧拉筛中的内层循环由于少了一个等号导致每次枚举都会少枚举已经得到的数,注意“少了一个等号会导致每次都少枚举已经得到的数字”,由缺少一个等号可以自然想到每次都少枚举了最后一个已经得到的数字,那么就是外层循环刚刚得到的最新的这个素数!因此整个欧拉筛误判了所有的素数的平方并把它们当成了素数,因此筛出1e6以内的所有素数并取平方就得到了LIST!对于给出的n和m,利用二分查找找到位置即可,然后的操作进行NTT对于998244353取模(原根为3)就得到了答案。

代码:
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int mann = 1e7+5;
int tot;
ll pri[mann];
bool is[mann];

void init () {
is[0] = is[1] = 1;
for (int i = 2; i < mann; ++i) {
if (!is[i]) {
pri[++tot] = i;
}
for (int j = 1; j <= tot && i*pri[j] < mann; ++j) {
is[i*pri[j]] = 1;
if (i % pri[j] == 0) {
break;
}
}
}
for (int i = 1; i <= tot; ++i) {
pri[i] *= pri[i];
}
}

void exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return;
}
ll x0, y0;
exgcd(b, a % b, x0, y0);
x = y0;
y = x0 - (ll)(a / b) * y0;
}

ll Inv(ll a, ll p) {
ll x, y;
exgcd(a, p, x, y);
x %= p;
while (x < 0) {
x += p;
}
return x;
}

ll qpow(ll a, ll b, ll p) {
if (b < 0) {
b = -b;
a = Inv(a, p);
}
ll ans = 1, mul = a % p;
while (b) {
if (b & 1) {
ans = ans * mul % p;
}
mul = mul * mul % p;
b >>= 1;
}
return ans;
}

#define maxn (65537*2)
const int MOD = 998244353, G = 3;

ll rev[maxn];

void get_rev(ll bit) {
for (ll i = 0; i < (1 << bit); i++) {
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}
}

ll ar[maxn], br[maxn];

void ntt(ll *a, ll n, ll dft) {
for (ll i = 0; i < n; i++) {
if (i < rev[i]) {
swap(a[i], a[rev[i]]);
}
}
for (ll step = 1; step < n; step <<= 1) {
ll wn;
wn = qpow(G, dft * (MOD - 1) / (step*2), MOD);
for (ll j = 0; j < n; j += (step<<1)) {
ll wnk = 1;
for (ll k = j; k < j + step; k++) {
ll x = a[k] % MOD, y = (wnk * a[k + step]) % MOD;
a[k] = (x + y) % MOD;
a[k + step] = ((x - y) % MOD + MOD) % MOD;
wnk = (wnk * wn) % MOD;
}
}
}
if (dft == -1) {
ll nI = Inv(n, MOD);
for (ll i = 0; i < n; i++) {
a[i] = a[i] * nI % MOD;
}
}
}

int main() {
init();
ll n, m;
while (scanf("%lld %lld", &n, &m) != EOF) {
memset(ar, 0, sizeof(ar));
memset(br, 0, sizeof(br));
memset(rev, 0, sizeof(rev));
int pos1 = lower_bound(pri+1, pri+1+tot, n)-pri;
int pos2 = upper_bound(pri+1, pri+1+tot, m)-pri-1;
int len = pos2-pos1+1;
for (int i = 0; i < len; ++i) {
ar[i] = br[i] = pri[pos2-i];
}
ll bit, s = 2;
for (bit = 1; (1 << bit) < 2*len-1; ++bit) {
s <<= 1;
}
get_rev(bit);
ntt(ar, s, 1);
ntt(br, s, 1);
for (ll i = 0; i < s; i++) {
ar[i] = ar[i] * br[i] % MOD;
}
ntt(ar, s, -1);
for (int i = 0; i < 2*len-2; ++i) {
cout << ar[i] << ' ';
}
cout << ar[2*len-2] << '\n';
}
return 0;
}

吐槽:

  1. 办比赛真麻烦,以后不想搞了。。

  2. 这些人怎么回事?学了这么久,除了暴力啥也不会?这样子别说区域赛了,省赛都打不了

  3. 头真铁啊,最后一题都加强数据rejudge了还想着莽过去。。

  4. 大家好像都很喜欢排序?异或的题我排序,二分的题我排序,模拟的题我也排序……

  5. 这次17的整体表现令人害怕,这样下去怕要被18踩爆;18也就前几各成绩还行,不过也合乎常理,毕竟走到最后的大概率都是这些人中的吧;柳总还是强啊;李浩然这小伙子不错.jpg,虽然这次比赛打的和狗屎一样,李浩然怎么起伏这么大.jpg。

  6. 这学期的训练就此结束了,总的来说确实比之前累了不少,不过同时也是有收获的吧。所谓“欲戴王冠,必承其重”,大概也能懂得一些了。

END:

所以说,知道自己的真实实力最重要,不要因为一时的失败就觉得自己不行。

一定要多试几次你才会知道,自己真的不行。

Donate comment here
0%