2017后期 第 1 场训练赛

题目依次为 NKOJ 上 P3496 P4236 P3774 P2407

1.数三角形

方法很多, 比如推出三边 x y z 的限制关系, 然后加加减减得到计算式子

不过也可以用观察法, 暴力计算出 n 为 1 至 13 对应的结果为:

0  0  0  1  3  7  13  22  34  50  70  95  125

相邻两数差为:

  0  0  1  2  4  6  9  12  16  20  25  30

这些相邻两数相邻差又为:

    0  1  1  2  2  3  3  4  4  5  5

找到规律了, 如果结果第 i 项为第 i - 1 项的值加上 plus , 则每次 plus 增加的值为 (i / 2 - 1)

比如结果第 7 项值为 13 , 上一项值为 7 , 此时 plus 为 6 , 上一次的 plus 为 4 , 两个 plus 的差正正好为 (7 / 2 - 1) = 2

写出递推算法,时间复杂度 O(n) .

 1 #include <stdio.h>
 2
 3 long long int ans, plus;
 4 int n;
 5
 6 int main()
 7 {
 8     int i;
 9     scanf("%d", &n);
10     for (i = 4; i <= n; ++i)
11         ans += plus += (i >> 1) - 1;
12     printf("%lld\n", ans);
13     return 0;
14 }

P1

2.翻硬币

SG(i) 表示硬币在左起第 i 位的局面

0 对应 SG 的值为 0, SG(0) = 0

1 后续局面为 0 , 所以 SG(1) = mex(SG(0)) = 1

01后续局面为 00, 10, 所以 SG(2) = mex(SG(0), SG(1)) = 2

001 后续局面为 000, 100, 010, 所以 SG(3) = mex(SG(0), SG(1), SG(2)) = 3

0001 后续局面为 1000, 0100, 0010, 所以 SG(4) = mex(SG(1), SG(2), SG(3)) = 4

...

推导出 SG(i) = i mod 5

将正面朝上的硬币 SG 值按位异或就可以得出总的 SG 值了

 1 #include <stdio.h>
 2
 3 int n, k, ans;
 4
 5 int main()
 6 {
 7     int i, j;
 8     scanf("%d", &k);
 9     while (k--) {
10         ans = 0;
11         scanf("%d", &n);
12         for (i = 1; i <= n; ++i) {
13             scanf("%d", &j);
14             if (j)
15                 ans ^= i % 5;
16         }
17         if (ans)
18             printf("Yes\n");
19         else
20             printf("No\n");
21     }
22     return 0;
23 }

P2

3.小鸟

动态规划, 用单调队列优化

f[i] 表示停留在第 i 棵树上的最小疲劳值, 可以得到方程:

f[i] = min{ f[j] + h[j]>=h[i]?1:0 }

i 的范围是 [1, n], j 的范围是 [i-k, i-1]

裸动规的时间复杂度是 O(nk) , 可以过 60% 的数据, 用单调队列优化后时间复杂度是 O(n)

构造一个根据 f 值递增的单调队列, 记录树的编号, 当队首对应树的编号小于 i - k 时说明无法飞到第 i 棵树, 将队首出队;

操作之后的队首对应的就是可以转移到 f[i] 的最小 f[j] 值, 再根据高度判断疲劳值是否 +1, 然后将 f[i] 对应树的编号在保

持单调性的情况下入队

注意, 为了使 f[i] 尽量小, 即疲劳值尽量不增加, 应该使第 j 棵树的高度尽量高, 因此单调队列还应该在 f 相同时把 d (即树

高度)较大的放在队首

 1 #include <stdio.h>
 2
 3 int n, q, k;
 4 int d[500005], f[500005], que[500005];
 5
 6 int main()
 7 {
 8     int i, j, head, tail;
 9     scanf("%d", &n);
10     for (i = 1; i <= n; ++i)
11         scanf("%d", &d[i]);
12     scanf("%d", &q);
13     for (i = 1; i <= q; ++i) {
14         scanf("%d", &k);
15         f[1] = 0;
16         head = tail = 1;
17         que[tail++] = 1;
18         for (j = 2; j <= n; ++j) {
19             while (j - k > que[head])
20                 ++head;
21             f[j] = f[que[head]] + (d[que[head]] <= d[j]);
22             while (head < tail && (f[j] < f[que[tail - 1]] || f[j] == f[que[tail - 1]] && d[j] >= d[que[tail - 1]]))
23                 --tail;
24             que[tail++] = j;
25         }
26         printf("%d\n", f[n]);
27     }
28     return 0;
29 }

P3

4.乘车路线

法一.搜索 + 剪枝

首先随意用一种图论算法(Floyd-Warshall 也行, 毕竟 n 最大 100, 但我还是写了个 SPFA)得到起点到终点最小费用, 如果大于 k,

说明钱带得实在太少啦, 跑不动! 如果钱足够的话,就开始搜索咯

用 sid_w[i] 记录点 i 到终点 n 的最小费用

用 sid_t[i] 记录点 i 到终点 n 的最短长度(原题是“长度”,可训练赛的时候被改成了“时间”)

如果走最便宜的路钱还不够, 即 当前剩余费用 - 欲搜索道路费用 < 搜索目标点到终点最小费用 ,剪枝

如果走最短的路还比已经得出的合理最短路程远,  即 当前已走长度 + 欲搜索道路长度 >= 合理最短长度, 剪枝

另外还有一点就是搜索某个点时最好先标记, 搜索完之后再取消标记, 避免回路和环

  1 #include <cstdio>
  2 #include <algorithm>
  3
  4 const int INF = 100000000;
  5
  6 int n, k, m, ans = INF;
  7 int que[1000005], sid_w[1000005], sid_t[1000005];
  8 int fst[105], nxt[10005], u[10005], v[10005], w[10005], t[10005];
  9 int anti_fst[105], anti_nxt[10005];
 10 bool bok[1000005], flag[1000005];
 11 char tt[30];
 12
 13 inline void getnum(int &num)
 14 {
 15     char tt;
 16     while ((tt = getchar()) < ‘0‘ || tt > ‘9‘);
 17     num = tt - ‘0‘;
 18     while ((tt = getchar()) >= ‘0‘ && tt <= ‘9‘)
 19         num = num * 10 + tt - ‘0‘;
 20     return ;
 21 }
 22
 23 inline void putnum(int num)
 24 {
 25     int top = 0;
 26     do
 27         tt[++top] = num % 10;
 28     while (num /= 10);
 29     while (top)
 30         putchar(tt[top--] + ‘0‘);
 31     return ;
 32 }
 33
 34 void anti_SPFA(int beg, int *dis, int *weight)
 35 {
 36     int head, tail, i, node;
 37     //init
 38     for (i = 1; i <= n; ++i) {
 39         dis[i] = INF;
 40         bok[i] = false;
 41     }
 42
 43     dis[beg] = 0;
 44     head = tail = 1;
 45     que[tail++] = beg;
 46     bok[beg] = true;
 47     while (head < tail) {
 48         bok[node = que[head++]] = false;
 49         for (i = anti_fst[node]; i; i = anti_nxt[i]) {
 50             if (dis[node] + weight[i] >= dis[u[i]])
 51                 continue;
 52             dis[u[i]] = dis[node] + weight[i];
 53             if (bok[u[i]])
 54                 continue;
 55             que[tail++] = u[i];
 56             bok[u[i]] = true;
 57         }
 58     }
 59     return ;
 60 }
 61
 62 void dfs(int node, int rem, int tim)
 63 {
 64     if (node == n) {
 65         ans = std:: min(ans, tim);
 66         return ;
 67     }
 68     int i;
 69     for (i = fst[node]; i; i = nxt[i])
 70         if (!flag[v[i]] && rem - w[i] >= sid_w[v[i]]
 71         && tim + t[i] + sid_t[v[i]] < ans) {
 72             flag[v[i]] = true;
 73             dfs(v[i], rem - w[i], tim + t[i]);
 74             flag[v[i]] = false;
 75         }
 76     return ;
 77 }
 78
 79 int main()
 80 {
 81
 82     int i, t1, t2, j;
 83     getnum(k);
 84     getnum(n);
 85     getnum(m);
 86
 87
 88     for (i = 1; i <= m; ++i) {
 89         getnum(u[i]);
 90         getnum(v[i]);
 91         getnum(t[i]);
 92         getnum(w[i]);
 93         nxt[i] = fst[u[i]];
 94         fst[u[i]] = i;
 95         anti_nxt[i] = anti_fst[v[i]];
 96         anti_fst[v[i]] = i;
 97
 98     }
 99
100     anti_SPFA(n, sid_w, w);
101     if (sid_w[1] > k) {
102         printf("NO\n");
103         return 0;
104     }
105
106     anti_SPFA(n, sid_t, t);
107
108     dfs(1, k, 0);
109     putnum(ans);
110
111     return 0;
112 }

P4_(1)

法二.二维最短路

dis[i][cost] 表示起点到 i 点在费用最大为 cost 的情况下的最短长度, 跑一个 SPFA 或者 Dijkstra 之后答案是 dis[n][k]

我写的是 SPFA :

 1 #include <stdio.h>
 2
 3 const int INF = 100000000;
 4
 5 int fst[100005], nxt[100005], v[100005], l[100005], t[100005];
 6 int dis[10005][10005], que[100005];
 7 int n, k, w, u;
 8 bool bok[100005];
 9
10 void SPFA(int beg)
11 {
12     int i, j, head, tail, node;
13     for (i = 1; i <= n; ++i) {
14         bok[i] = false;
15         for (j = 0; j <= w; ++j)
16             dis[i][j] = INF;
17     }
18     for (j = 0; j <= w; ++j)
19         dis[beg][j] = 0;
20     head = tail = 1;
21     que[tail++] = beg;
22     bok[beg] = true;
23     while (head < tail) {
24         bok[node = que[head++]] = false;
25         for (i = fst[node]; i; i = nxt[i])
26             for (j = t[i]; j <= w; ++j) {
27                 if (dis[node][j - t[i]] + l[i] >= dis[v[i]][j])
28                     continue;
29                 dis[v[i]][j] = dis[node][j - t[i]] + l[i];
30                 if (bok[v[i]])
31                     continue;
32                 que[tail++] = v[i];
33                 bok[v[i]] = true;
34             }
35     }
36     return ;
37 }
38
39 int main()
40 {
41     int i;
42     scanf("%d%d%d", &w, &n, &k);
43     for (i = 1; i <= k; ++i) {
44         scanf("%d%d%d%d", &u, &v[i], &l[i], &t[i]);
45         nxt[i] = fst[u];
46         fst[u] = i;
47     }
48     SPFA(1);
49     if (dis[n][w] != INF)
50         printf("%d\n", dis[n][w]);
51     else
52         printf("NO\n");
53     return 0;
54 }

P4_(2)

改悔:

1题这类找通项公式题可以暴力算些结果再观察规律嘛, 毕竟对于数学渣来说推式子挺耗时间的

搜索剪枝一定要剪干净啊!剪一半真的只有 50 分, 不标记已访问点导致程序原地转圈也是!

说到二维最短路, 我已经不会写次短路的代码了!

单调队列一年前就看过课件,结果忘光了, DP + 单调队列 很常见嘛

题目:

P3496数三角形
时间限制 : 10000 MS   空间限制 : 65536 KB
评测说明 : 时限1000ms
问题描述

给出一个正整数n,从1,2,3.....n 中选出三个不同整数,使得以它们为三边长可以组成三角形,问
总共有多少种不同的三角形?
例如,n=5 时有三种:(2,3,4) , (2,4,5) , (3,4,5)

输入格式

一个正整数n

输出格式

一个整数,表示三角形的个数

样例输入 1

5

样例输出 1

3

样例输入 2

30

样例输出 2

1925

样例输入 3

8

样例输出 3

22

提示

【数据范围】
对于30%的数据,3<=n<=100
对于100%的数据,3<=n<=1,000,000

P4236翻硬币
时间限制 : - MS   空间限制 : 165536 KB 
评测说明 : 1s
问题描述

两个玩家在玩一个有趣的翻硬币游戏。

有 N 枚硬币排成一排,有的正面朝上,有的反面朝上。从左往右硬币按1 到N 编号。玩家轮流操作。每次操作,玩家选一枚正面朝上的硬币,将它翻转,同时在该硬币左侧连续四个硬币中,再任选一个硬币,将其翻转。
    具体而言,假设第i号硬币正面朝上。若将第i号硬币翻转后,必须在编号为i-1,i-2,i-3,i-4的四个硬币中选一个进行翻转。若i<=4,则可只翻转i号硬币,也可以再在1到i-1之间选一个进行翻转。

谁没有硬币可翻谁就算输。两个玩家都非常聪明,问先手是否获胜?

输入格式

第一行,一个正整数T,表示接下来有T组测试数据。对于每组测试数据:

第1行,一个整数N,表示硬币的数量。
第2行,N个空格间隔的整数(0和1),从左往右依次表示游戏开始前硬币的情况,其中数字0表示正面朝下,数字1表示正面朝上。

输出格式

T行,每行对应一组测试数据的答案。若先手胜输出”Yes” 否则输出“No”

样例输入

5
9
1 0 1 1 1 0 1 0 0 
13
0 0 1 1 1 1 0 1 1 1 0 0 1 
12
1 0 1 0 1 0 1 1 1 0 0 0 
9
0 0 0 0 1 0 0 0 0 
13
1 0 1 0 1 1 0 0 0 0 0 0 1

样例输出

Yes
Yes
Yes
No
No

提示

对于30%的数据:  1≤N≤100

对于100%的数据: 1≤N≤100000 ,T≤10

P3774小鸟
时间限制 : - MS   空间限制 : 65536 KB 
评测说明 : 1000ms
问题描述

有一排n棵树,第i棵树的高度是Di。
一群小鸟要从第1棵树飞到第n棵树去玩。
不同小鸟的飞跃能力不同,第i只小鸟的飞跃能力为ki,表示如果当前它位于第x号树,那么它可以飞到x+1,x+2,......,x+ki号树上去,也就是一次可以飞过ki棵树。
如果小鸟飞到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。
小鸟们希望最小化劳累值,请你计算每只小鸟达到终点所需最小劳累值。

输入格式

第一行,一个整数N(2<=N<=100000)
第二行,N个空格间隔的整数,第i个数表示第i棵树的高度Di。(1<=Di<=10^9)
第三行,一个整数Q(1<=Q<=25),表示小鸟的数量
接下来Q行,每行一个整数,其中第i个整数表示第i只小鸟的飞跃能力ki。

输出格式

Q行,每行一个整数,表示对应小鸟的劳累值

样例输入

9
4 6 3 6 3 7 2 6 5
2
2
5

样例输出

2
1

提示

第1只小鸟停落的树编号为1, 3, 5, 7, 8, 9.在3飞到5时劳累值+1,在7飞到8时劳累值+1



来源  POI 2014 Little Bird

P2407乘车路线
时间限制 : 10000 MS   空间限制 : 165536 KB
问题描述

编号为 1.. N 的N座城镇用若干仅供单向行驶的道路相连,每条道路上均有两个参数:道路长度(length)和在该条道路上行驶的费用(cost)。
BOB 准备从城镇 1 出发到达城镇 N,但他目前只有 W 块钱,为此,你需要帮助他寻找一条从城镇1到城镇 N 在他能支付的前提下的一条最短路线。

输入格式

第一行为钱的数目W (0<=w<=1000)
第二行为城镇数目N(2<=N<=100)
第三行为为道路条数K(1<=K<=10000)
随后的 K 行每行为一条道路的信息,包含 4个数值(S,D,L,T)其中S为道路的起点, D为道路的终点 , L为道路长度, T为所需支付 费用。
(1<=S,D<=N,1<=L<=100,0<=T<=100)

输出格式

输出最短长度,若无解,则输出“NO”;

样例输入




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

样例输出

11



来源  CEOI1998

时间: 2024-12-26 11:08:05

2017后期 第 1 场训练赛的相关文章

UPC2018组队训练赛第七场

题目来自ICPC 2017 Japan Tsukuba A题:Secret of Chocolate Poles 有三种巧克力,分别是厚度为1的白色巧克力,厚度为1或者k的黑色巧克力.要求把巧克力放到高度为 l 的盒子里,并且要黑白相间,底部和顶部必须都是黑色的 当l=1,ans=1:当l<k,ans=(l-1)/2+1:当l=k,ans=(l-1)/2+2;当l>k时,就可以转化成排列组合问题了,枚举厚度为k的黑色巧克力数目i,然后对于每一种情况,再枚举厚度为1的黑色巧克力的数目j,那么此时

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&#39;s problem(manacher+二分/枚举)

HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分相同,第一部分与第二部分对称. 现在给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法,求出以第i个点为中心的回文串长度,记录到数组p中 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样. 因为我们已经记录下来以

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&amp;#39;s problem(manacher+二分/枚举)

pid=5371">HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分同样,第一部分与第二部分对称. 如今给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法.求出以第i个点为中心的回文串长度.记录到数组p中 要满足题目所要求的内容.须要使得两个相邻的回文串,共享中间的一部分,也就是说.左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也

Dream_Chaser队训练赛第一场 I题

Dream_Chaser队训练赛第一场 I题 题目来自2012成都区域赛 I - Count Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4472 Description Prof. Tigris is the head of an archaeological team who is currently in charge of a

早晨训练赛第一场 B题 哈希

早晨训练赛第一场 B题 B - Trees in a Row Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 402B Description The Queen of England has n trees growing in a row in her garden. At that, the i-th (1 ≤ i 

HDU 4864 Task (贪心+STL多集(二分)+邻接表存储)(杭电多校训练赛第一场1004)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4864 解题报告:有n台机器用来完成m个任务,每个任务有一个难度值和一个需要完成的时间,每台机器有一个可以工作的最长时间和一个可以完成的任务的难度的最大值, 一台机器能完成一个任务的条件是这台机器的最长工作时间和能完成任务的难度值必须都大于等于这个任务,而且一台机器最多完成一个任务,假设一个任务的时间为t,难度值为x,那么完成这个任务可以赚到的钱 money = 500 * t + 2 * x; 现在

HDU 4902 Nice boat 2014杭电多校训练赛第四场F题(线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 解题报告:输入一个序列,然后有q次操作,操作有两种,第一种是把区间 (l,r) 变成x,第二种是把区间 (l,r) 中大于x的数跟 x 做gcd操作. 线段树区间更新的题目,每个节点保存一个最大和最小值,当该节点的最大值和最小值相等的时候表示这个区间所有的数字都是相同的,可以直接对这个区间进行1或2操作, 进行1操作时,当还没有到达要操作的区间但已经出现了节点的最大值跟最小值相等的情况时,说明

HDU 4941 Magical Forest(map映射+二分查找)杭电多校训练赛第七场1007

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4941 解题报告:给你一个n*m的矩阵,矩阵的一些方格中有水果,每个水果有一个能量值,现在有三种操作,第一种是行交换操作,就是把矩阵的两行进行交换,另一种是列交换操作,注意两种操作都要求行或列至少要有一个水果,第三种操作是查找,询问第A行B列的水果的能量值,如果查询的位置没有水果,则输出0. 因为n和m都很大,达到了2*10^9,但水果最多一共只有10^5个,我的做法是直接用结构体存了之后排序,然后m

Contest1657 - 2019年我能变强组队训练赛第十四场

Contest1657 - 2019年我能变强组队训练赛第十四场 Similarity of Subtrees #include <bits/stdc++.h> using namespace std; typedef unsigned long long ull; typedef long long ll; const int maxn=100100; ull ha[maxn]; vector<int>E[maxn]; unordered_map<ull,int>m;