HAOI2013题解

HAOI2013 的特点

题水

谁说的。。。
做了大半年的题你跟我说水。。。
欸,似乎真的很水。。。
大概是我太水吧。。。

T1 [HAOI2013] 跑步训练

题目描述

Dr.Kong准备参加冬季越野比赛。为了能在比赛中有好的发挥,他决定每天早上上班前在附近的一条山路上开始训练。他当然希望每次训练中跑得尽可能远,但他的时间有限,每天早上跑步训练的时间不得超过M秒 (1 <= M <= 10,000,000)。
为了合理的安排跑步路程,Dr.Kong将整条山路划分成T个长度相同的小段(1 <= T <= 100,000),并且,分别用u,f,d这3个字母之一来表示每个小段是上坡、平地,或是下坡。 Dr.Kong要花t1秒(1 <= t1 <= 100)才能跑完一段上坡路,跑完一段平地的耗时是
t2秒(1 <= t2 <= 100),跑完一段下坡路要花t3秒(1 <= t3 <= 100)。注意,沿山路原路返回的时候,原本是上坡路的路段变成了下坡路,原本是下坡路的路段变成了上坡路。
Dr.Kong想知道,在不超过M秒内返回的前提下,他最多能在这条山路上跑多远。

输入格式

第1行: M T t1 t2 t3 (数据之间用一个空格隔开)

第2..T+1行: 每行为1个字母u或f或d,描述了相应段的山路路况(上坡、平地,或是下坡)

输出格式

输出1个整数,为Dr.Kong在不超时回到的前提下,最多能跑到几段。

样例输入

13 5 3 2 1
u
f
u
d
f

样例输出

3

cogs评分半星。。。

解法

就是用一个整型s存达到当前步数要走的来回的耗时
然后每走一段路程加上耗时与返回时的耗时,s > M 并且step <= t时结束程序,输出step - 1

贴代码

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
/*kZime*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#define MAXN
using namespace std;
inline int read() {
int k = 0, f = 1; char c = getchar();
for(; !isdigit(c); c = getchar())if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) k = k * 10 + c - '0';
return k * f;
}
inline char in_char() {
char c = getchar();
for(; !isalpha(c); c = getchar());
return c;
}
/-----------------------------------------------------------------------------*/
int v[3] , m, t, s = 0, step = 0;// v[3] 表示3种地形的速度
char c[100233];
int main() {
#ifndef MYLAB
freopen("HAOI2013T1.in", "r", stdin);
freopen("HAOI2013T1.out", "w", stdout);
#else
freopen("in.txt", "r", stdin);
#endif
m = read();
t = read();
for(int i = 0; i <= 2; i++) {
v[i] = read();
}
for(int i = 0; i < t; i++) {
c[i] = in_char();
}
while(s <= m && step <= t) {
if(c[step] != 'f') { // 如果step这一段地形不是平地,总耗时s增加v[0] + v[2]
s += v[0] + v[2];
}
else {//如果是平地,则总耗时增加两倍的平地耗时
s += v[1] << 1;
}
step++;
}
printf("%d",--step);
return 0;
}

T2[HAOI2013] 花卉节

题目描述

ZZ市准备在绿博园举办一次花卉节。Dr.Kong接受到一个任务,要买一批花卉进行布置园林。
能投入买花卉的资金只有B元 (1 <= B <= 10^18) 。Dr.Kong决定做一个社会调查,统计一下市民们都喜欢哪种花卉,以便在有限的资金范围内,让更多的市民都能找到并标注一盆自己喜欢的花卉(一盆花只能一位市民标注)。
经调查统计,市场上有N (1 <= N<=100,000)种不同类型的花卉,第i种花卉的价格是Pi(1 <= Pi <= 10^18) 。有Ci (1 <= Ci <= 10^18) 个市民喜欢。
你能帮助Dr.Kong计算一下,在不透支的情况下,如何购买花卉才能让更多的市民都能找到并标注一盆自己喜欢的花卉?
例如:Dr.Kong 有 50块钱,有5种不同类型的花卉:
HAOI2013T2
显然,Dr.Kong不能购买第5种类型的花卉,因为他不够钱。
下面的购买方案是最优的:
第1种花卉买3盆;第2种花卉买1盆;第3种花卉买2盆;第4种花卉买2盆。
总共花费:5_3+1_1+10_2+7_2=50,这样,Dr.Kong 最多能让3+1+2+2 =8 人满意。

输入格式

第1行: N B
第2..N+1行: Pi Ci (i=1,2,….,N)。

输出格式

一个整数,最多可以让多少市民满意。

样例输入

5 50
5 3
1 1
10 4
7 2
60 1

样例输出

8

解法

算是一道很简单的贪心吧(是吧,反之就是很水,但因为要用到高精所以不水了(其实还是很水吧。。。
先将所有花的种类按照价格从小到大排序,然后尽可能多买便宜的花就行了,所以真的水我也就WA了3遍

贴代码

先把低精的代码贴上来

低精50分版

高精版随后贴上3/20/2017 6:30:43 PM

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
/*kZime*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <cstdlib>
#include <algorithm>
#define MAXN 1000000
using namespace std;
inline int read() {
int k = 0, f = 1; char c = getchar();
for(; !isdigit(c); c = getchar())if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) k = k * 10 + c - '0';
return k * f;
}
/*-----------------------------------------------------------------------------*/
struct f{
int p, w;
}fl[MAXN];
int n, m;
bool cmp(f a, f b) {
return a.w < b.w;
}
int main() {
#ifndef MYLAB
freopen("haoi13_t2.in", "r", stdin);
freopen("haoi13_t2.out", "w", stdout);
#else
freopen("in.txt", "r", stdin);
#endif
n = read();
m = read();
for(int i = 0; i < n; i++) {
fl[i].w = read();
fl[i].p = read();
}
sort(fl, fl + n, cmp);
int s = 0, i = 0, ans = 0;
while(s <= m && i < n) {
int temp = fl[i].p * fl[i].w;
if(temp + s < m) {
s += temp;
ans += fl[i].p;
i++;
}
else {
ans += (m - s) / fl[i].w;
break;
}
}
printf("%d", ans);
return 0;
}

高精全A版

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
/*kZime*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <cstdlib>
#include <algorithm>
#define MAXN 1000000
#define ull unsigned long long
#define ll long long
using namespace std;
ull read() {
ull k = 0, f = 1; char c = getchar();
for(; !isdigit(c); c = getchar())if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) k = k * 10 + c - '0';
return k * f;
}
/*-----------------------------------------------------------------------------*/
struct f{
ull p, w;
}fl[MAXN];
ull n, m;
class bn{
public:
ull s[101], l;
string str;
bn() {
memset(s, 0, sizeof(s));
l = 1;
}
bn operator + (const ull &b) {
s[0] += b;
for(int i = 0; i < l || s[i]; i++) {
s[i + 1] += s[i] / 10;
s[i] %= 10;
if(i > l) l = i;
}
l++;
while(!s[l - 1]) l--;
return *this;
}
void output() {
for(int i = l - 1; i >= 0; i--) {
cout << (int) s[i];
}
}
};
bool cmp(f a, f b) {
return a.w < b.w;
}
int main() {
#ifndef MYLAB
freopen("haoi13_t2.in", "r", stdin);
freopen("haoi13_t2.out", "w", stdout);
#else
freopen("in.txt", "r", stdin);
#endif
n = read();
m = read();
for(ull i = 0; i < n; i++) {
fl[i].w = read();
fl[i].p = read();
}
sort(fl, fl + n, cmp);
ull s = 0, i = 0;
bn ans;
while(s <= m && i < n) {
ull temp = fl[i].p * fl[i].w;
if(temp + s < m) {
s += temp;
ans = ans + fl[i].p;
i++;
}
else {
ans = ans + (m - s) / fl[i].w;
break;
}
}
ans.output();
return 0;
}

long long 全A版

打完高精之后看了看天天的rank2代码突然发现不加高精也能过,然后就换了个结构自己码了一遍,但跑的还是没有天天的快,气哭

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
/*kZime*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <cstdlib>
#include <algorithm>
#define MAXN 1000000
#define ll long long
using namespace std;
inline ll read() {
ll k = 0, f = 1; char c = getchar();
for(; !isdigit(c); c = getchar())if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) k = k * 10 + c - '0';
return k * f;
}
/*-----------------------------------------------------------------------------*/
struct f{
ll p, w;
}fl[MAXN];
ll n, m;
bool cmp(f a, f b) {
return a.w < b.w;
}
int main() {
#ifndef MYLAB
freopen("haoi13_t2.in", "r", stdin);
freopen("haoi13_t2.out", "w", stdout);
#else
freopen("in.txt", "r", stdin);
#endif
n = read();
m = read();
for(ll i = 0; i < n; i++) {
fl[i].w = read();
fl[i].p = read();
}
sort(fl, fl + n, cmp);
ll i = 0, ans = 0;
while(i < n) { //花种类总数n 当前剩余可支配资金m
ll temp = m / fl[i].w; //选择当前花种最多可以满足市民总数temp
if(temp > fl[i].p) {
ans += fl[i].p;
m -= fl[i].p * fl[i].w;
i++;
}
else {
ans += temp;
break;
}
}
printf("%lld", ans);
return 0;
}

T3[HAOI2013] 开关控制

题目描述

元宵节快要到了,某城市人民公园将举办一次灯展。Dr.Kong准备设计出一个奇妙的展品,他计划将编号为1到N的N(1 <= N <= 35)盏灯放置在一个有M条(1 <= M <= 595)边连接的网络节点上。

每盏灯上面都带有一個开关。当按下某一盏灯的开关時,这盏灯本身以及与之有边相连的灯的状态就会改变。状态改变指的是:当一盏灯是亮时,就会被关闭;当一盏灯是关闭时,就会被打开亮着。

现在的问题是,你能帮助Dr.Kong计算一下最少要按下多少个开关,才能把所有的灯都打开亮着(初始状态:所有的灯都是关闭的)。

数据保证至少有一种按开关的方案,使得所有的灯都能被重新打开。

输入格式

第1行: N M
第2到第M+1行:每一行有两个由空格隔开的整數,表示两盏灯被一条边连接。

输出格式

一个整数,表示要把所有的灯都打开时,最少需要按下的开关次数。

样例输入

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

样例输出

3

解法

听天天讲了好久终于搞明白怎么做了。。。
首先,证明每个灯按2次之后的的状态和按2次前的状态一样,因此每个灯最多按一次
其次,想要让保证当前灯的状态是亮着,前提条件是与这个灯相连的灯(包括当前灯)中有奇数个灯的按钮被按过。设g[i][j]表示灯i是否连接到灯j,x[i]表示i这个灯是否被按过。由这条性质,我们发现,可以列出一个方程(xor 为 异或^)
异或的性质:相同为0,不同为1
异或方程组
然后就是运用高斯消元,讲方程系数消到一个上三角矩阵。 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for(int i = 1; i <= n; i++) {//循环的是列
free_yuan[i] = 1; //初始化i行是自由元
for(int j = i; j <= n; j++) {//从i~n循环行
if(!a[j][i]) continue;//如果第j行第i个系数为0,继续
for(int k = i; k <= n; k++) {//交换发现i行j行
swap(a[i][k], a[j][k]);
}
swap(a[i][0], a[j][0]);//a[i][0]存的是第i行的解(1),别人的代码可能是a[i][n + 1]
break;
}
if(!a[i][i]) continue; //这行存在有自由元
else {
free_yuan[i] = 0;
for (int j = i + 1; j <= n; j++) {//进行消元操作
if (!a[j][i]) continue;
for (int k = i; k <= n; k++) a[j][k] ^= a[i][k];
a[j][0] ^= a[i][0];
}
}
}

消元之后,因为存在自由元的i行的灯xi可选择开,也可选择不开,因此要进行dfs找结果
代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void dfs(int now, int tmp) {
if(tmp > ans) return;
if(now <= 0) {
ans = min(ans, tmp);
return;
}
//深搜边界
if(free_yuan[now]) {//如果now行为自由元
slt[now] = 1;
dfs(now - 1, tmp + 1);
slt[now] = 0;
dfs(now - 1, tmp);
}
else {//slt[now]为灯now的状态
slt[now] = a[now][0];
for(int i = now + 1; i <= n; i++) {//利用slt[now]化简now行之前的方程组
if (a[now][i]) slt[now] ^= slt[i];
}
dfs(now - 1, tmp + slt[now]);
}
}
int main(){
dfs(n, 0);
}

T4[HAOI2013] 软件安装

解法

需要用到区间的查询与更改,所以自然而然的想到了线段树,但是困难的是线段树要维护什么。这道题要求查询符合长度要求的空区间的起始位置,所以要维护m:区间内最长空区间的长度,mp:区间最长空区间的起始位置。因为一定会有一段区间的最长空区间跨过两个子区间,而又不是两个子区间的最长空区间的情况,所以还要维护两个值lm:区间左端点之后的最长连续空区间的长度,rm:到右端点的最长连续空区间的长度。有了这4个值,才能对区间的最长空区间的起始位置进行查找。还有一点要注意的是因为优先选择左边的可行区间的起点,所以逻辑判断时候要考虑上这种情况。

代码

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
138
139
140
141
142
143
144
145
146
147
148
149
150
/*kZime*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <cstdlib>
#include <algorithm>
#define MAXN
using namespace std;
inline int read() {
int k = 0, f = 1; char c = getchar();
for(; !isdigit(c); c = getchar())if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) k = k * 10 + c - '0';
return k * f;
}
/*-----------------------------------------------------------------------------*/
int n, m;
class segtree {
public:
int l, r, lm, rm, m, mp, tag; //mp指的是最长区间的起始位置
segtree() {
tag = -1;
}
void in(int _l, int _r, int _ml, int _mr, int _m, int _mp, int _tag) {
l = _l;
r = _r;
lm = _ml;
rm = _mr;
m = _m;
mp = _mp;
tag = _tag;
}
}nd[300050];
void build(int x, int l, int r) {
if(l == r) {
nd[x].in(l, r, 1, 1, 1, l, -1);
}
else {
int mid = (l + r) / 2;
int len = r - l + 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
nd[x].in(l, r, len, len, len, l, -1);
}
}
void push_down(int k) {
if(nd[k].tag == 1) {//该区间已被占满
nd[k].lm = nd[k].rm = nd[k].m = nd[k].mp = -1;
nd[k << 1].tag = 1;
nd[k << 1 | 1].tag = 1;
nd[k].tag = -1;
}
if(nd[k].tag == 0) {//该区间为空区间
nd[k << 1].tag = nd[k << 1 | 1].tag = 0;
nd[k].lm = nd[k].rm = nd[k].m = (nd[k].r - nd[k].l + 1);
nd[k].mp = nd[k].l;
nd[k].tag = -1;
}
}
void push_up(int k) {
push_down(k);
push_down(k << 1);
push_down(k << 1 | 1);
int x1 = nd[k << 1].m , x2 = nd[k << 1].rm + nd[k << 1 | 1].lm, x3 = nd[k << 1 | 1].m;
if(x1 >= x2 && x1 >= x3) { // 注意!!!这三个判断是有优先级的
nd[k].m = x1; // 优先级 x1 > x2 > x3
nd[k].mp = nd[k << 1].mp;
}
else if(x2 >= x3) {
nd[k].m = x2;
nd[k].mp = nd[k << 1].r - nd[k << 1].rm + 1;
}
else {
nd[k].m = x3;
nd[k].mp = nd[k << 1 | 1].mp;
}
int lenl = nd[k << 1].r - nd[k << 1].l + 1;
int lenr = nd[k << 1 | 1].r - nd[k << 1 | 1].l + 1;
nd[k].lm = nd[k << 1].lm;
if(nd[k << 1].lm == lenl) {
nd[k].lm = max(nd[k].lm, nd[k << 1].lm + nd[k << 1 | 1].lm);//一定要用max()函数,被占区间的rm可能为-1
}
nd[k].rm = nd[k << 1 | 1].rm;
if(nd[k << 1 | 1].rm == lenr) {
nd[k].rm = max(nd[k].rm, nd[k << 1 | 1].rm + nd[k << 1].rm);
}
}
int query() {
push_down(1);
return nd[1].m;
}
int get_the_smallest_mp (int k, int len) {
push_down(k);
if(nd[k].m < len) return 0;
else if(nd[k].lm >= len) return nd[k].l;
else if(nd[k << 1].m >= len) return get_the_smallest_mp(k << 1, len);
else if (nd[k << 1].rm >= 0 && nd[k << 1 | 1].lm >= 0 && nd[k << 1].rm + nd[k << 1 | 1].lm >= len) return (nd[k << 1].r - nd[k << 1].rm + 1);
else return get_the_smallest_mp(k << 1 | 1, len);
}
void change(int k, int l, int r, int tag) { //更新区间
push_down(k);
if(l <= nd[k].l && nd[k].r <= r) {
nd[k].tag = tag;
return;
}
else if(l > nd[k].r || r < nd[k].l) {
return;
}
change(k << 1, l, r, tag);
change(k << 1 | 1, l, r, tag);
push_up(k);
}
int main() {
#ifndef MYLAB
freopen("haoi13t4.in", "r", stdin);
freopen("haoi13t4.out", "w", stdout);
#else
freopen("in.txt", "r", stdin);
#endif
n = read();
m = read();
build(1, 1, n);
for(int i = 1; i <= m; i++) {
int type = read();
if(type == 1) {//安装应用
int len = read();
if(query() < len) {
printf("0\n");
}
else {
int sm =get_the_smallest_mp(1, len);
printf("%d\n", sm);
change(1, sm, sm + len - 1, 1);//tag 1 表示占用该区间
}
}
else {
int x = read();
int len = read();
change(1, x, x + len - 1, 0);//tag 0 清空该区间
}
}
return 0;
}

T5[HAOI2013] 遥控器

解法

据说floyd也可以过,感觉好神奇,然而我打了一个模拟,要注意的细节很多

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
/*kZime*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <cstdlib>
#include <algorithm>
#define MAXN
using namespace std;
inline int read() {
int k = 0, f = 1; char c = getchar();
for(; !isdigit(c); c = getchar())if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) k = k * 10 + c - '0';
return k * f;
}
/*-----------------------------------------------------------------------------*/
bool num[10], up, down, _, na[100]; // nc[i] (num cost)从x到i的最小代价
// na[i] (num able)是否能到达i
int x, y, ans, nc[100];
void init() {
for(int i = 1;i <= 3; i++) {
num[i] = read();
}
up = read();
for(int i = 4; i <= 6; i++) {
num[i] = read();
}
down = read();
for(int i = 7; i <= 9; i++) {
num[i] = read();
}
_ = read();
num[0] = read();
x = read();
y = read();
}
void units() {
for(int i = 0; i <= 9; i++) {
if(num[i]) {
nc[i] = 1;
na[i] = 1;
}
}
}
void tens() {
if(!_)return;
for(int i = 1; i <= 9; i++) {
if(num[i]) {
for(int j = 1; j <= 9; j++) {
if(num[j]) {
nc[i * 10 + j] = 3;
na[i * 10 + j] = 1;
}
}
}
}
}
int dis_up(int a, int b) { //从a一路up到b的代价
if(b >= a)return b - a;
else return 100 + b - a; //up到99后回到0
}
int dis_down(int a, int b) { //从a一路down到b的代价
if(a >= b) return a - b;
else return 100 + a - b; //down到0后变成99
}
void try_num() { //尝试从x直接到y
if(na[y]) {
ans = min(nc[y], ans);
}
}
void try_up() {
if(!up) return;
for(int i = 0; i <= 99; i++) {
if(na[i]) {
ans = min(ans, nc[i] + dis_up(i, y));
}
}
}
void try_down() {
if(!down) return;
for(int i = 0; i <= 99; i++) {
if(na[i]) {
ans = min(ans, nc[i] + dis_down(i, y));
}
}
}
int main() {
#ifndef MYLAB
freopen("HAOI2013T5.in", "r", stdin);
freopen("HAOI2013T5.out", "w", stdout);
#else
freopen("in.txt", "r", stdin);
#endif
ans = 0x7fffffff;
init();
units();
tens();
na[x] = 1;
nc[x] = 0;
try_num();
try_up();
try_down();
if(ans == 0x7fffffff)printf("-1");
else printf("%d", ans);
return 0;
}

写在最后

话说模块化代码真爽啊