首页 > 其他分享 >骗分宝典(How to get IOI-Au)

骗分宝典(How to get IOI-Au)

时间:2023-02-27 18:58:02浏览次数:45  
标签:std 骗分 get 元素 得分 DFS How pts

0. 前言

骗分,是 OI 中特有的得分方式,旨在使用简单的非正解获取更多的分数。毕竟并不是所有人都可以在赛场上就可以想到满分正解的,并且还要考虑时间分配问题。因此,熟练掌握骗分方法,让自己在赛场上尽力得分,这是 OIers 的基本素养。(ACM 没有部分分,所以无法施展骗分妙招 /oh)

本文将会为大家讲解常用的骗分瞎搞技巧 / 算法 / 数据结构,祝各位 CSP/NOIp/NOI 以及其它所有比赛 rp++!

1. 基础骗分技巧

1.1 "不可以,总司令"

在有些题目中,会有这么一句话:

若无解,请输出 -1/NO

这让许多没有任何头绪或时间不够没法写正解的 OIers 欣喜若狂。因为这意味着,只需要:

printf("-1");

你就可以获得 10 分、20 分甚至更多!

值得一提的是,在 CSP-S 2022 T3 星战 一题中,虽然每个测试点多组数据,但是只要全部输出 NO,你还是可以获得 45 分!这也是本章标题的来历

通常数据没这么水,所以期望得分:

10 pts ~ 30 pts

1.2 样例

样例是很有用的,因为这可以帮你初步 debug 程序,还可以骗分!

在美国的 USACO 中,第一个测试点 必定是样例。也就是说,在一题中只要输出样例,你就可以获得 10pts!

当然 CSP 啥的就没这个规定了。/oh

期望得分:

0 pts ~ 10 pts

另:多找一找样例规律,也许是诈骗题。

1.3 打表——无赖但好用

打表,就是把答案手算或暴力算出来,然后把已经算出来的答案塞进一个数组里,对于每个输入,输出算好的答案。

这在小数据中及其好用,例如:NOIp 2003 普及组 T3 栈

本题看着挺唬人的,不过就是一道卡特兰数板子。当然,普及组选手不一定会这些。这题数据异常的小,我们直接模拟先算出来,然后放进一个数组里,就可以静待 AC 啦!

当然,打表也不一定要打全部,可能会爆代码长度上限,可以只打部分分。在提高组及以上,打表也通常用来找规律写出状态转移方程啥的。

期望得分(能使用的话):

30 pts ~ 100 pts

1.5 欧皇专属——随机数

请注意,在您没有满足以下任意一个条件时,请不要随便使用此方法:

  • CSGO 一发出高价红 / 粉皮;
  • CODM 一发出神话;
  • 原神一发出五星;
  • 其他类似的……

你可以用 C 标准库中的 srand()rand() 函数,也可以使用 C++11 的 std::mt19937 类。使用指南可参考 OI-wiki 或 CppReference。

当然数据范围越小越好,例如输出 Yes/No 又找不出规律时,随机就成为了首选!

期望得分:

Random numbers from 0 to 100 pts

接下来会涉及到基础算法,请刚学完语法的萌新止步于此。/fad

2. 基础骗分算法

2.1 模拟

这个应该没什么应该说的吧,照着题目要求模拟就行。(

较繁琐的模拟就不是骗分了,赤裸裸的例子:SDOI 2010 猪国杀

模拟通常用来骗高级数据结构和高级算法的分,如:ST 表、线段树、平衡树等等。

例题:[USACO 20007 JAN] Balanced Lineup G

本题是 RMQ(区间极值问题)的板子,神犇可以手搓线段树、ST 表,蒟蒻的我们,还是骗分吧。

while (q--) {
	scanf("%d%d", &a, &b);
	int minn = INT_MAX, maxn = INT_MIN;
	for (int i = a; i <= b; i++) {
		if (h[i] < minn)minn = h[i];
		if (h[i] > maxn)maxn = h[i];
	}
	printf("%d\n", maxn - minn);
}

本程序得分:50 pts。

期望得分:

20 pts ~ 40 pts

2.2 DFS:万能算法

DFS 是图论中的重要算法,但我们看来,图论神马的都是浮云,关键就是如何骗分。正如你所见,DFS 是适用性最高的算法之一。像是 DP、数学题、诈骗题,大多都可以 DFS!

例题 1:NOIp 2005 普及组 采药

本题是 0/1 背包的板子题,不过对于不知 DP 为何物的我们来说,DFS 成了不错的选择。

DFS 的万能 + 简易可能不太明显?接下来以一道状态压缩 DP 例题作为我们下一个 DFS 骗分目标(不是真题):洛谷 P1433 吃奶酪

本题 DFS 简单好写,且可通过所有普通数据

void dfs(int step, int now, double sum) {
	if (sum >= ans)
        return;
	cnt++;
	if (cnt >= 17000000) {
		printf("%.2Lf", ans);
		exit(0);//卡时间,递归到一定次数直接输出当前结果
	}
	if (step > n) {
		if (sum < ans)ans = sum;
		return;
	}
	for (int i = 1; i <= n; i++) 
		if (!f[i]) {
			sum += dis[now][i];
			if (sum >= ans)
                continue;
			f[i] = 1;
			dfs(step + 1, i, sum); 
			f[i] = 0; 
			sum -= dis[now][i];
		}
}

可惜的是,本题增加了 hACk 数据,本代码无法 AC。

期望得分:

20 pts ~ 40 pts

2.3 BFS

和 DFS 差不多,多用在图论。

期望得分:

20 pts ~ 40 pts

2.4 下下策:Floyd

如果一道图论题需要求最短路,但是不会 dijkstra,或负权图不会 bellman ford(SPFA 是队列优化 bf),可以考虑使用 Floyd。虽然在稀疏图效率变低很多,但是总比全丢分强吧?

无向图板子:

void floyd () {
    for (int k = 1; k <= n; ++k)
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                if (j != i && j != k && i != k)
                    f[j][i] = f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}

期望得分(假设正解需要用到的是 dij):

20 pts ~ 30 pts

3. 高级骗分技巧

3.1 数据点分治

注:本骗分方法和分治 & 点分治没啥关系。

有时候我们口胡出正解或贪心解等快速解法了!但是有可能假掉或对代码实现的正确性不敢保证。/oh/fad

这时候,我们就需要 数据点分治 了!

数据点分治的代码结构大概是这样的:

if (n < 骗分能过范围) {
    ...;//骗分程序
} else {
    ...;//正确性有待证明的快速解法  
}

当然你还可以 if..else if..else if..else 这样套着玩!

期望得分:

暴力法得分 ~ 100 pts

当然不一定是快速解法,也可以是……

if (是样例) {
    printf("%d", 样例);
} else if (n < 暴力能过) {
    ...;//暴力程序
} else if (打表范围) {
    printf("%d", ans[n - 1]);
} else ...//其他的

也可以称之为数据点分治。

3.2 常数优化

有很多,但是不建议各位乱用,可能会负优化不要以为你比编译器更懂优化

3.2.1 快读快写

顾名思义,就是加快 IO 速度。

第一种方法:关闭同步流

在主函数开头加上这个(C++ 98 请把 nullptr 换成 NULL):

ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(nullptr);

就可以加快 IOStream 的速度!

注意:永远也不要混用 C 风格 StdIO 和 C++ 风格 IOStream!

第二种方法:fread()

请注意这货是 StdIO 的。

char buf[1 << 20], *_now = buf, *_end = buf;
#define getchr() (_now == _end && (_end = (_now = buf) + fread(buf, 1, 1 << 20, stdin), _now == _end) ? EOF : *_now++)
int read () {
	int x = 0, f = 1;
	char ch = getchr();
	while (ch < '0' || ch > '9') {
		if (ch == '-')
			f = -1;
		ch = getchr();
	}
	while (ch >= '0' && ch <= '9')
		x = x * 10 + ch - '0', ch = getchr();
	return x * f;
}

不使用传统快读的原因是因为优化甚至不如 IOStream 关同步流。

3.2.2 适当的位运算

请不要滥用。因为可能会负优化

通常在线段树之类的常数怪兽使用。接下来我给大家列出几个常用的加速位运算。

  • n & 1,判断 n 奇偶性。奇数返回 1;
  • n << k,返回 n 乘 2 的 k 次方;
    • k << 1 以及 k << 1 | 1,线段树的节点 k 的左儿子右儿子;
  • n >> k,返回 n 除以 2 的 k 次方。

3.3 猜测、找规律

有时候,满足题目有解的条件十分苛刻,我们可以“猜测”答案大概率为 YES/NO,并且赌数据较水,没有过多特殊构造的,从而实现总司令可获得的分数变多。这类题目通常在提高组及以上,且不会太多。

比如,满足 CSP-S 2022 T3 星战 有解的情况比较苛刻,CCF 又有随机脚本造数据,因此出现这么水的数据。/oh

至于找规律,通常配合打表使用,一般是用于找出数列本身的规律(如:卡特兰数),亦或者口胡动态转移方程,这再上一章提到过了。此外:珍爱生命,谨防诈骗题。没头绪或数据范围极大记得找规律啊,大概率是诈骗。

期望得分(找出来的话):

10 pts ~ 40 pts

4. STL & pb-ds

4.1 STL

STL 是个好东西,封装了常用算法(如快排)和常用数据结构(如栈、堆等),有助于节省码量、节约时间和骗分

STL 相关部分参考 CppReference

4.1.1 std::deque

注意:std::deque 空间常数很大。

std::deque 实现了双端队列。

常用操作:

  • at() / operator[],随机访问;
  • front() / back(),前后元素;
  • push_front() / push_back(),前后端插入;
  • pop_front() / pop_back(),前后端弹出;
  • size() / empty(),元素个数以及是否为空;
  • insert(),插入元素;
  • 各种迭代器相关的……

用途:主要是单调队列。

4.1.2 std::stack

注意:stack 基于 std::deque,空间常数很大。

std::stack 实现了栈。

常用操作:

  • top(),栈顶元素;
  • pop() / push() ,弹出栈顶以及入栈;
  • size() / empty(),元素个数以及是否为空;

4.1.3 std::queue

注意:queue 基于 std::deque,空间常数很大。

std::queue 实现了队列。

常用操作:

  • front() / back(),队首和队尾元素;
  • pop() / push() ,弹出队首以及入队;
  • size() / empty(),元素个数以及是否为空;

4.1.4 std::priority_queue

std::priority_queue 实现了优先队列。

第三个参数类似于 std::sort() 的第三个参数,填充的是比较类。

  • top(),(暂时先这么说)堆顶元素;
  • pop() / push() ,弹出堆顶以及入堆;
  • size() / empty(),元素个数以及是否为空;

4.1.5 std::set & std::multiset & unordered_set

set 用红黑树实现了“集合”,multiset 则是“可重集”。unordered set 则是“无序集合”

set 和 multiset 因为底层是 rbt,所以会自动排序。unordered set 底层是 Hash,所以不会自动排序。

常用操作:

  • insert(),插入;
  • erase() / clear(),删除和清空;
  • size() / empty(),元素个数以及是否为空;
  • count(),返回指定元素在集合内的个数;
  • find(),查找某个指定元素;
  • lower_bound()upper_bound(),二分查找首个不小于和大于指定元素的元素;
  • 各种迭代器相关的……

例题

  • HNOI 2002 营业额统计

你知道吗:这题就是因为 set 能过所以降级到了绿。

  • 洛谷 P1738 洛谷的文件夹

  • 珂朵莉树的习题后文会提到。

4.1.6 std::map & std::multimap & std::unordered_map

map 用红黑树实现了“映射”,multimap 则是“可重映射”。unordered map 则是“无序映射”

map 和 multimap 因为底层是 rbt,所以会自动排序。unordered map 底层是 Hash,所以不会自动排序。

常用操作:

  • insert(),插入;
  • erase() / clear(),删除和清空;
  • size() / empty(),元素个数以及是否为空;
  • count(),返回指定元素在映射内的个数;
  • find(),查找某个指定元素;
  • lower_bound()upper_bound(),二分查找首个不小于和大于指定元素的元素;
  • at() / operator[],访问键值映射的元素;
  • 各种迭代器相关的……

例题

其实大部分字典树的题都可以用 map 的。/oh

  • TJOI 2010 阅读理解

    set + map 或 map + vector。

  • 洛谷 P2580 于是他错误的点名开始了


待更新……

标签:std,骗分,get,元素,得分,DFS,How,pts
From: https://www.cnblogs.com/FiveUnderlines/p/how-to-get-ioi-au.html

相关文章