停课期间-考试总结Ⅰ

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27121257/article/details/82759129

申明:由于本人有点强迫症,可能要改完题目才会写总结,因此会晚一点

考试的execel就不写了,直接写在这里算了,(没有latex写公式是真的不爽)
如果发现本人的题解与下文雷同,不用说了,我直接蒯的



9.17

总的来说,代码的实现并不困难,但思维难度偏大。
在本次考试中,有些该拿得分没有拿到,该打的暴力却没打,自以为的正解却挂了。看来下次还是稳点好,还是老老实实的把暴力码完再说吧

  • 洛谷 P1662 数7
     正解为玄学的打表(头痛.jpg):利用分段打表,锁定离答案较近的初始位置,确保每次暴力枚举的次数不会超时。(每隔 1 0 6 10^6 打一个表,记录方向和位置)
     顺便说说本人90分的搜索:利用数字中含有7的要求,用dfs实现从高位到底位枚举报数,一旦某一位出现7,则直接处理跳过(规律很简单),从八开始,注意不要跳过答案即可。【简而言之就是利用规律,跳过所有含有7的报数】

  • 洛谷 P1665 正方形计数
     首先吐槽一下数据, n 4 n^4 的暴力竟然也能水过。
     对于判断正方形/矩形,本人建议利用对角线来判断(防止重复计算
     考场做法:利用 v e c t o r vector 聚集线段中点相同的对角线,然后判断对角线相等且垂直,结果 50 50 ,具体原因位置,目测时斜率判断的锅(看来 d o u b l e double 能不用就不用)
     更正做法:利用 v e c t o r vector 聚集长度的对角线,直接判断中点重合与四边相等

  • 洛谷 P1666 前缀单词
     看到前缀,考场上确实想到了建议一棵字典树,然而,也就只是建了一颗字典树。后面在那里推数学方法,而且还推错了。。。。( 30 30 还不如暴力 Q W Q QWQ
     正解:建树后考虑树型 D P DP ,令 D P [ i ] DP[i] 表示子树 i i 中子集数
      D P [ i ] = j 为i的孩子 D P [ j ] + 1 DP[i] = \prod_{j\text{为i的孩子}}DP[j] + 1
     解释:前半部分为不选 i i 的方案数(每个孩子的情况相互独立,直接利用乘法原理),后面的 1 1 表示,选 i i 的方案数(其孩子均不能选)
     注意:上述式子中的 i , j i,j 均为一个单词的结尾。因此在建完字典树后,要么省略掉那些不是单词结尾的节点,重新建一棵树,要么在从下往上传递的时候,判断 1 1 是否要加上

  • 洛谷 P1667 数列
     本题需要用到前缀和,如果没有想到,真的连搜索的不会搜。(懵逼.jpg)
     将题中的操作用前缀和表示的话,很容易得到这么一个结论(自己推吧,码字太累了):对于区间 [ x , y ] [x,y] 的操作只是将 s u m [ x 1 ] , s u m [ y ] sum[x - 1], sum[y] 交换了一个位置
     对此,题目就转化为:用最少的次数交换前缀和序列,使之满足单调递增的要求。(对于一下两种情况无解:1.前缀和出现负数 2.前缀和相等)
     当然,由于只是相对大小的关系,可以离散化(对后面的求解有优化)
     对于求最小交换次数:考虑用贪心求解。
    贪心思路:保证每一次交换至少有一个点到正确的位置
    离散化后代码十分简单:

        for(int i = 1; i <= n; ++i)
        for(;i != sum[i];)
        {
            swap_(sum[i], sum[sum[i]]);
            ++ans;
        }
    

9.18

恭喜打脸,恭喜被虐。看来我并不适合做 D P DP Q A Q QAQ )。额,最后两题都看出来了是 D P DP ,就是码不对(推了个半真半假的转移方程),第二题也有点无脑了,忘了 p p 不一定在 [ l , r ] [l,r] 内。

  • 洛谷 P1620 漂亮字串
    算是数学题吧,直接处理出 O O X X 可分段数的范围。然后分类讨论一下即可
  • 洛谷 P1621 集合
    利用埃氏筛法边筛边合并即可,其实只要将 p r i m e [ i ] × ( j 1 ) prime[i] \times (j - 1) 与  p r i m e [ i ] × j prime[i] \times j 合并即可
  • 洛谷 P1622 释放囚犯
    区间 D P DP :倒着来这来就是一个石子合并
    f [ l ] [ r ] f[l][r] 表示合并区间 [ l , r ] [l,r] 的最小花费
    转移方程: f l , r = f l , k + f k + 1 , r + s u m l , r + l e n 2 f_{l,r} = f_{l, k} + f_{k + 1,r} + sum_{l,r} + len - 2
    P s . Ps.   s u m i , j sum_{i,j} 表示区间未在释放名单上的人, l e n 2 len-2 表示在释放名单上,但还是要吃肉的人
  • 洛谷 P1623 树的匹配Treasury
    树型 D P DP :方程有点复杂,同时要高精度
    状态: f [ u ] [ 0 / 1 ] f[u][0/1] 表示点 u u 是否参与匹配的最大匹配数, g [ u ] [ 0 / 1 ] g[u][0/1] 表示对应的方案数
    定理: f [ u ] [ 0 ] f [ u ] [ 1 ] f[u][0] \leq f[u][1] (其实无所谓,只不过是几个 i f if 的事)
    转移方程:
    f [ u ] [ 0 ] = f [ v ] [ 1 ] f[u][0] = \sum f[v][1]
    f [ u ] [ 1 ] = max ( f [ v ] [ 1 ] f [ p ] [ 1 ] + f [ p ] [ 0 ] ) , p v f[u][1] = \max(\sum f[v][1] - f[p][1] + f[p][0]), p\in v
    至于 g g 的方程,用计数原理推吧.不过要之一细节
    D P DP 代码:(没用 + = += = *= 是因为懒得重载了)
IL void dfs(int u)
{
	//这个是用来找根节点的,请跳过(默认为1)
	if(vis[u]) return ;
	vis[u] = 1;
	//初始化
	g[u][0] = g[u][1] = one;
	int tmp;
	for(int i = last[u], v; (v = to[i]); i = nxt[i])
	{
		dfs(v);
		//优化一下求f[u][1]的过程:u是否当前的当前的v匹配
		//在之前已经有过v和u匹配了,那么,当前的v可以不和u匹配
		if(f[u][1])
		{
			f[u][1] += f[v][1];
			//注意相等的时候,两种选择均可,故要加上
			if(f[v][0] ^ f[v][1]) g[u][1] = g[u][1] * g[v][1];
			else g[u][1] = g[u][1] * (g[v][0] + g[v][1]);
		}
		
		//当前v和u匹配的情况
		//由于f[u][0]放在后面更新,因此,此时的f[u][0] = sigma(f[v][1])
		tmp = f[u][0] + f[v][0] + 1;
		if(tmp > f[u][1])
		{
			f[u][1] = tmp; g[u][1] = g[u][0] * g[v][0];
		}else
		if(!(tmp ^ f[u][1]))
			g[u][1] = g[u][1] + (g[u][0] * g[v][0]);
		//处理f[u][0]的情况
		f[u][0] += f[v][1];
		if(f[v][0] ^ f[v][1]) g[u][0] = g[u][0] * g[v][1];
		else g[u][0] = g[u][0] * (g[v][0] + g[v][1]);
	}
	//显而易见,若u不可能和子节点相连,则不存在方案
	if(!f[u][1]) g[u][1] = zero; 
}

9.19

这次题目还算简单,基础分 g e t get ,然而 d p dp 还是没有想出来( % Q Z Q \% QZQ 大佬, D P DP 鬼才)。

  • 洛谷 P1627 中位数
    将小于中位数变为 1 -1 ,大于中位数的变为 1 1 ,然后向左/右扫一遍,直接用乘法原理统计答案即可(保证包含中位数的某一段区间和为0即可)
  • 洛谷 P1437 敲砖块
    本题的关键在于消除后效性,对此,换一个角度来找关系
    状态: f [ j ] [ i ] [ k ] f[j][i][k] 表示第 j j 列敲了 i i 块总共敲了 k k 块的最大值
    转移: f [ j ] [ i ] [ k ] = f [ j + 1 ] [ l ] [ k i ] + t = 1 i n u m [ t ] [ j ] , l [ i 1 , n j ] f[j][i][k] = f[j + 1][l][ k - i] + \sum_{t = 1}^{i} num[t][j], l \in [i - 1, n - j]
  • 洛谷 P1628 合并序列
    标题与内容有什么联系么, excuse me?
    排一遍序,直接找就是了
  • 洛谷 P1629 邮递员送信
    正反图各跑一遍最短路即可,表示结构体封装起来还是要爽一些

9.20

被虐的体无完肤。考试的时候陷入了诡异的怪圈(看题 \to 好像有点思路的样子 \to 又好像不行的样子 \to 再想想 \to 然后?两小时过去有了 \dots ),四题都想了半个小时,结果都没能想出来(要么思路接近但没有拓展下去,要么完全没有思路)

  • 洛谷 P1645 序列
    令人头痛的题,考试的时候自动忽略的"至少"二字,结果贪心没贪出来(本来就菜),差分约束也没想到(如果看到这两个字的话)
    差分约束:令 f [ i ] f[i] 表示 [ 1 , i ] [1,i] 有多少个元素,则对于题目的 ( L i , R i , C i ) (L_i, R_i, C_i) ,可以转化为 f [ R i ] f [ L i 1 ] C i f[R_i] - f[L_i - 1] \geq C_i ,然后利用 f f 数组的定义,又有等式: 0 f [ i ] f [ i 1 ] 1 0 \leq f[i] - f[i - 1] \leq 1 。综上,利用给出的不等式建图跑 s p f a spfa 即可

  • 洛谷 P1191 矩形
    考试的时候一直想着用悬线法求最大全0矩阵,但奈何不会解决重复计算的问题(好吧,不是不会,容斥是真的太复杂了,不想码,多半会出锅)。结果,数据水得只要枚举 + + 优化就能过(┑( ̄Д  ̄)┍)
    n 3 n^3算法 :确定左右/上下边界,再扫一遍,顺便统计答案。

  • 洛谷 P1647 锁
    玄学的题目,貌似要找规律,目前洛谷几乎无人AC( 3 3 A C AC ,我们机房贡献了两个,然而代码是蒯的),网上目前只找到了一份没有详细说明代码,暂时未改,有空再去研究吧
    U p d 9.25 : Upd\quad 9.25:
    终于改完了,果真是到规律题
    一时兴起,写了篇blog

  • 洛谷 P1648 看守
    确定是一道数学题,也想到了要正负分类讨论,但就是没有进一步展开公式。
    推导过程:首先从二维的角度来看 x i x j + y i y j \mid x_i - x_j \mid + \mid y_i - y_j \mid 去掉绝对值后只需要在一下四种情况中取最大值即可:
    x i x j + y i y j ( x i + y i ) ( x j + y j ) x_i - x_j + y_i - y_j \Leftrightarrow (x_i + y_i) - (x_j + y_j)
    x i x j y i + y j ( x i y i ) ( x j y j ) x_i - x_j - y_i + y_j \Leftrightarrow (x_i - y_i) - (x_j - y_j)
    x i + x j + y i y j ( x i + y i ) ( x j + y j ) -x_i + x_j + y_i - y_j \Leftrightarrow (-x_i + y_i) - (-x_j + y_j)
    x i + x j y i + y j ( x i y i ) ( x j y j ) -x_i + x_j - y_i + y_j \Leftrightarrow (-x_i - y_i) - (-x_j - y_j)
    经过进一步的拓展可发现,对于每个坐标,只需要进行 2 D 2^D D D 为维度)种情况的枚举即可,最后将每种情况极差的最大值即可


9.21

看来我是真的不适合做贪心一类的玄学题(智商啊~头痛 . j p g .jpg )。
然而一道我曾经出过数据的水题都码错了,我还能说些什么呢(好吧,这题是正的不想做,随便码了一下,那么长代码都没有检查一下,然后 f l a g flag 就倒了)

  • P1109 学生分组
    签到水题(给低保),过

  • P1134 阶乘问题
    简明的题意:求阶乘数最右边一个非零数字
    申明:考试时的数据为 N 1 0 2009 N \leq 10^{2009} ,一看就是道毒瘤题。
    阶乘分解什么的都没有用,上网搜了一下,貌似要用到数学推导
    根据结论: ( 5 k + t ) ! ( 5 k ) ! t ! 2 k t ! k ! (5k+t)! \approx (5k) \cdot !t! \approx 2^k \cdot t! \cdot k!
    \approx 表示末尾非0位相等(姑且这么表示吧)
    n n 转化为 5 k + t 5k+t 的形式,对 k ! k! 递归处理
    PS:由于太过毒瘤,有空再补,暂未确定该方法的可行性(感觉空间和时间都得完)
    U p d 9.25 : Upd\quad 9.25:
    f l a g flag 成立。实测上述方法 50 50 。在网上找了找类似的方法,时间复杂度基本都是 l o g 5 ( n ) log_5(n) 的,不过还有些常数上的优化(比如, / 5 /5 等价于 × 2 \times 2 后右移一位)
    交数据在洛谷上测试的情况还行吧,算了,也就这样吧。
    在这里插入图片描述

  • P1649 [USACO07OCT]障碍路线Obstacle Course
    最少转弯次数。 z z zz 的题目 z z zz 的我,这都能打错。(好吧,主要是不想码这题)

  • P1650 田忌赛马
    贪心/ D P DP ?不存在的,某位大佬直接 n 2 n^2 暴力过了(感觉会是近似单峰的函数,或许可以优化,但没有实现)
    贪心思路:
    S1:田忌最快的马比齐王最快的马快,直接用这匹马。
    S2:田忌最快的马比齐王最快的马慢,用最慢的马去抵掉
    S3:如果相同,要是田忌最慢的马比齐王最慢的马快,这两匹马抵掉
    S4:否则,用田忌最慢的马去抵掉齐王最快的马


9.22

一如既往地被虐,表示心累 ┭┮﹏┭┮。(重点是思路基本上都想到了,就是某些细节上出了岔子)。但从某种意义上来说,也可能是理解的不够深入。

  • 洛谷 P1658 购物
    贪心题:首先确定一点, 1 1 一定要有,否则无解。
    若当前已经能够凑出 [ 1 , S ] [1,S] 的数值,那么我们再加入一枚面值为 x x 的硬币,则可以凑出$$
    其实在敲搜索的时候就已经想到了这一点上,但由于加了初始时加了枚举,也就忽略掉了正解的思路。
  • 养猪
    根据相邻交换法可以证明 p p 值大的放在前面,然后 01 01 背包就好了。
    然而,我忘了取最大值(好吧,这一点确实是对DP理解得不够深入)
  • 洛谷 P1660 数位平方和
    记忆化搜索:如果纯粹的递归的话会出现环,根据公式的定义也容易知道,一个环内的 h ( x ) h(x) 值是一样的。对此,就必须要保证找两圈(第二圈找最小值,第一圈环),如果只是绕一圈就返回的话,只能保证环的起点为正确值,其余的点虽然被标记了,但未必是最真正的最小值。
    恩,我就是只搜了一圈就返回的那个
  • 洛谷 P1661 扩散
    根据题目中的图应该能找到规律:在 t t 时刻,点 ( x i , y i ) (x_i,y_i) 能够到达的点 ( x , y ) (x,y) 满足条件 x i x + y i y t \mid x_i - x\mid + \mid y_i - y\mid \leq t
    对于两点 A ( x i , y i ) , B ( x j , y j ) A(x_i, y_i), B(x_j, y_j) ,令 t t 时刻它们恰好在点 C ( x , y ) C(x,y) 处相遇了,则满足: x i x + y i y t &amp; x j x + y j y t \mid x_i - x\mid + \mid y_i - y\mid \leq t \quad \&amp; \quad\mid x_j - x\mid + \mid y_j - y\mid \leq t
    两式相加得: x i x + y i y + x j x + y j y 2 t \mid x_i - x\mid + \mid y_i - y\mid +\mid x_j - x\mid + \mid y_j - y\mid \leq 2t
    易知,点 C C 绝对在点 A A 与点 B B 之间,则去掉绝对值后 x , y x,y 必定能消掉,则:
    x i x j + y i y j 2 t &ThickSpace; &ThickSpace; t   x i x j + y i y j 2 \mid x_i - x_j\mid + \mid y_i - y_j \mid \leq 2t \iff t \geq \ \frac{\mid x_i - x_j\mid + \mid y_i - y_j \mid}{2}
    因此,当 t = x i x j + y i y j 2 t = \lceil \frac{\mid x_i - x_j\mid + \mid y_i - y_j \mid}{2} \rceil A B AB 两点刚好联通。
    综上我们就可以快速的算出两点之间联通的最短时间了。
    对此,整个题目可以转化为:给定一个完全图,要求去掉若干条边使得图依旧联通且边权最大值最小。
    二分+并查集可行,但还有更加有效的方法:用 k r u s k a l kruskal 求一遍 M S T MST 即可

PS:原始数据貌似出锅了,洛谷上的数据经过了修改,应该是对的。


9.23

本次考试一般般吧,但有些该打的暴力还是没有打对
下午莫名的累,就随便写写好了

  • 洛谷 P2577 午餐
    又是一道 d p + dp+ 贪心的题(然而考试的时候并没有想到DP \to _ \to )。
    根据贪心的原则,吃放时间长的放在前面(相同则打饭时间短的先)
    DP的话,只要记录其中一个窗口即可,另外一个可以直接算出来
  • 洛谷 P1582 倒水
    一道玄学的数学题。给出 n n 个瓶子,则最少能剩下 c o u n t ( n ) count(n) 的瓶子( c o u n t ( n ) count(n) n n 在二进制下 1 1 的个数),于是乎,直接枚举计算即可(计算可用 l o w b i t lowbit 来优化)
  • 洛谷 P1624 单词缩写
    恶心的读入,坑爹的数据(有一个缩写就是 L A S T LAST ),这题的搜索我还打错了,心累。
    读入: s t r i n g s t r e a m stringstream 赛高,真的爽。
    D P DP :令 f [ i ] [ j ] [ k ] f[i][j][k] 表示缩写的第 i i 个字母与全称的前 j 1 j - 1 个单词外加第 j j 个单词的前 k k 个字母的所匹配的方案数。
    为了保证良好的刷题体验感,推荐使用刷表法
  • 洛谷 P1625 求和
    码到自闭的一题。
    首先很容易知道: 1 i = 1 m = ( 1 i = 1 m 1 1 i = 2 m ) × 1 m 1 \frac{1}{\prod_{i = 1}^{m}} = (\frac{1}{\prod_{i = 1}^{m - 1}} - \frac{1}{\prod_{i = 2}^{m}}) \times \frac{1}{m - 1}
    将其带入 S S ,通分后得: S = i = n + 1 m + n 1 i = 1 m 1 ( m 1 ) × i = n + 1 m + n 1 × i = 1 m 1 S = \frac{\prod_{i = n + 1}^{m + n - 1} - \prod_{i = 1}^{m - 1}}{(m - 1) \times \prod_{i = n + 1}^{m + n - 1} \times \prod_{i = 1}^{m - 1}}
    剩下的就交给高精度吧(好好享受 \逃)
    关于约分(两种解决方案):
    1)来一个 s u p e r G C D superGCD 和高精除高精
    2)由于分母中的最大的乘数为 n + m 1 n + m - 1 ,因此,直接枚举 [ 2 , n + m 1 ] [2,n+m-1] 中的质数,直接试除即可。

本人重载的高精度板子(部分)

struct Bigint
{
	int num[2500];
	int size;
	
	IL Bigint()
	{
		memset(num, 0, sizeof(num));
		size = 0;
	}
	
	bool operator < (const Bigint &b) const
	{
		if(size ^ b.size) return size < b.size;
		for(int i = size; i; --i)
		if(num[i] ^ b.num[i])
			return num[i] < b.num[i];
		return 0;
	}
	
	bool operator == (const Bigint &b) const
	{
		if(size ^ b.size) return 0;
		for(int i = size; i; --i)
		if(num[i] ^ b.num[i])
			return 0;
		return 1;
	}
	
	bool operator <= (const Bigint &b) const
	{
		if(size ^ b.size) return size < b.size;
		for(int i = size; i; --i)
		if(num[i] ^ b.num[i])
			return num[i] < b.num[i];
		return 1;
	}
	
	Bigint operator * (const Bigint &b)
	{
		if(b.size == 1 && b.num[1] == 1) return (*this);
		if(size == 1 && num[1] == 1) return b;
		Bigint c;
		int r = 0;
		int &s = c.size;
		
		for(int i = 1, p; i <= size; ++i)
		{
			p = i;
			for(int j = 1; j <= b.size; ++j, ++p, r/= 10)
			{
				r += num[i] * b.num[j] + c.num[p];
				c.num[p] = r % 10;
			}
			--p;
			for(; r; r /= 10)
			{
				r += c.num[++p];
				c.num[p] = r % 10;
			}
			if(p > s) s = p;
		}
		
		for(; s > 1 && !c.num[s]; --s);
		return c;
	}
	
	Bigint operator * (const int &b)
	{
		if(b == 1) return (*this);
		Bigint c;
		int r = 0;
		int &s = c.size;
		s = size;
		
		for(int i = 1; i <= size; ++i, r/= 10)
		{
			r += num[i] * b;
			c.num[i] = r % 10;
		}
		for(; r; r /= 10)
			c.num[++s] = r % 10;
		
		for(; s > 1 && !c.num[s]; --s);
		return c;
	}
	
	Bigint operator - (const Bigint &b)
	{
		//default: (*this) >= b
		Bigint c;
		
		int r = 0;
		int &s = c.size;
		s = size;
		for(int i = 1; i <= size; ++i)
		{	
			r += num[i] - b.num[i];
			if(r < 0)
			{	
				c.num[i] = r + 10;
				r = -1;
			}else
			{
				c.num[i] = r;
				r = 0;
			}
		}
		
		for(; s > 1 && !c.num[s]; --s);
		return c;
	}
	
	Bigint operator / (const ll &b)
	{
		if(b == 1) return (*this);
		
		Bigint c;
		ll r = 0;
		int &s = (c.size);
		s = -1;
		
		for(int i = size; i; --i)
		{
			r = r * 10 + num[i];
			if(r >= b)
			{
				if(s == -1) s = i;
				c.num[i] = r / b;
				r -= b * c.num[i];
			}
		}

		return c;
	}
	
	IL int div_cmp(const Bigint &b)
	{
		if(size < b.size) return 1;
		for(int i = 0; i < b.size; ++i)
		if(num[size - i] ^ b.num[b.size - i])
			return num[size - i] < b.num[b.size - i];
		return 0;
	}
	
	IL Bigint div_mul_10(int t)
	{
		if(!t) return (*this); 
		Bigint c;
		
		c.size = size + t;
		for(int i = 1; i <= size; ++i)
			c.num[i + t] = num[i];
		return c;
	}
	
	Bigint operator / (Bigint b)
	{
		// b | (*this)
		if(b.size == 1 && b.num[1] == 1) return (*this);
		
		Bigint a = (*this), c, tmp, r;
		int &s = c.size;
		s = -1;
		
		for(int len, t;;)
		{
			len = a.size - b.size - a.div_cmp(b);
			if(len < 0) break;
			tmp = b.div_mul_10(len);
			
			t = 0;
			for(;tmp <= a; a = a - tmp, ++t);
			
			if(s == -1 && t) s = len + 1;
			
			c.num[len + 1] = t;
		}
		
		
		return c;
	}
	
	IL void in(int x)
	{
		size = 0;
		for(; x; x /= 10)
			num[++size] = x % 10;
	}
	
	IL void out()
	{
		for(int i = size; i; --i)
			printf("%d", num[i]);
		printf("\n");
	}
	
	IL bool is_even()
	{
		//except zero
		if(size == 1 && !num[1]) return 0;
		return !(num[1] & 1);
	}
	
	IL int turn_int()
	{
		int sum = 0;
		for(int i = 1; i <= size; ++i)
			sum = sum * 10 + num[i];
		return sum;
	}
	
	//------text------
	IL void in()
	{
		char c[1000];
		scanf(" %s", c + 1);
		for(int i = strlen(c + 1); i; --i)
			num[++size] = c[i] - '0';
	}
};

一道较为简单的排序题,显而易见的,排序后相邻的两个来 P K PK 差值最小。
对此,我们只需要扫一遍求出所有相邻的差值后sort一遍求前 k k 小的和即可


9.24

今天的题目偏数学,不过总体来说还行吧。不过有看错了题目就有点尴尬了。

  • 洛谷 P1655 小朋友的球
    第二类string数,直接上公式记忆化搜索即可,不过要加上高精度。
    f [ n ] [ m ] = m × f [ n 1 ] [ m ] + f [ n 1 ] [ m 1 ] f[n][m] = m \times f[n - 1][m] + f[n - 1][m - 1] (第 m m 个箱子有球的情况 + + m m 个箱子没有球的情况)
    f [ n ] [ m ] f[n][m] 表示 n n 个不同的球放入 m m 个相同箱子里的方案数(不允许空箱)
  • 洛谷 P1630 求和
    利用同余性质 a b % p = ( a % p ) b % p a^b \% p = (a \% p) ^ b \% p 简化运算即可
  • 洛谷 P1631 序列合并
    考虑用堆来优化枚举,假设当前已经产生了 n n 个数且当前产生的数为 x x ,若 x x 大于 n n 个数中的最大值,则不必要继续枚举下去了(两个序列都先排序,保证枚举时单调递增)
  • 洛谷 P1632 点的移动
    看错题目了,以为是求前k个坐标的最小值(于是用对顶堆动态求中位数,结果还出锅了)。
    实际上是个暴力枚举的水题。易知, x , y x,y 是独立的,且对于每一维而言,“中点”(姑且算是吧)必定在已出现的点上。因此,我们只需要枚举每一个“中点”,然后求最小值即可。
  • 洛谷 P1633 二进制
    吐槽一句,考场上的样例出锅了, 7 7 写成 1 1 ,然而我的暴力还是错了。
    一道数位DP题(似乎之前都没怎么接触过)。
    f [ i ] [ a ] [ b ] [ c ] [ 0 / 1 ] f[i][a][b][c][0/1] 表示前 i 1 i-1 x x a a 1 1 y y b b 1 1 z z c c 1 1 ,且 z z 的第 i i 位是否有 1 1
    还是用刷表法简单点(可用滚动数组优化)
    具体的转移方程就不多说了,自己手推吧,主要说说思路
    0 / 1 0/1 的情况分开分析,对于 f [ i ] [ a ] [ b ] [ c ] [ 0 / 1 ] f[i][a][b][c][0/1] 来说,只有四种情况:
    1) a , b a,b 的第 i i 位均不放 1 1
    2)只有 a a 的第 i i 位放 1 1
    3)只有 b b 的第 i i 位放 1 1
    4) a , b a,b 的第 i i 位均放 1 1

9.25

宏定义有毒!
宏定义有毒!
宏定义有毒!
又是一起关于优先级的悲剧(好吧,下次一定要打括号)

吐槽一句:一套上古时期的题了,数据是真的水,而且还有点不规范。

  • 货物搬运
    环形的均分纸牌,拆环成链即可

  • 暴力摩托
    再次说明,宏定义真的有毒!;顺便吐槽一下数据,一半的数据点都不满足 S S 10 10 的倍数(去尾就好了)
    一道很水的 D P DP n 2 n^2 扫过去就好了

  • 射击比赛
    吐槽数据,太水了,而且 s p j spj 还要我自己写。
    这道题目的可做性还是不错的,总共有两种写法。
    网络流(二分图匹配):(其实考场上有想到二分图匹配,但时感觉匈牙利 O ( n m 2 ) O(nm^2) 的复杂度有点吃不消, D i n i c Dinic 由于太久没有,有点忘了)
    行一边,列一边,由列向行连两条边即可
    贪心?:其实有点像数独(启发式搜索),但可以通过证明保证不用回溯。
    h [ i ] h[i] 表示当前情况下,每行能够放置的白格数。
    m i n ( h [ i ] ) = { 0 , 无解 1 , 方案唯一 2 , 任意放置 min(h[i]) = \begin{cases} 0 , &amp; \text{无解} \\[2ex] 1 , &amp; \text{方案唯一} \\[2ex] \geq 2 , &amp; \text{任意放置} \end{cases}
    前两种情况均好解释,对于第三种情况:由于每一列只有两个白格,因此,任意放置后,依旧能够保证 m i n ( h [ i ] ) 1 min(h[i]) \geq 1 ,即有解,因此,任意放置均可。
    顺便附上 s p j spj (简易的判断,不要信错应(用脚写的))
    貌似一定要输出错应,否则算作“无效校验器”

#include <bits/stdc++.h>

using namespace std;

ifstream fin,fout,fstd;
ofstream fscore,freport;

string s1, s2; 
stringstream data;

bool mp[1005][1005];
bool use[1005];

bool WA1()
{
	freport << "read 'NO' or expect 'NO'";
	return 0;
}

bool WA2()
{
	freport << "wrong anwer(forgive my laziness)";
	return 0;
}

bool WA3()
{
	freport << "exist black block";
	return 0;
}

bool judge()
{
    int t;
    fin >> t;
    for(int f1, f2, n, m; t; --t)
    {
    	getline(fout, s1);
    	getline(fstd, s2);
    	f1 = (s1.find("NO") != -1);
    	f2 = (s2.find("NO") != -1);
    	if(f1 ^ f2) return WA1();
    	
    	memset(mp, 0, sizeof(mp));
    	memset(use, 0, sizeof(use));
    	fin >> n >> m;
    	for(int i = 1, x, y; i <= m; ++i)
    	{
    		fin >> x >> y;
    		mp[x][i] = mp[y][i] = 1;
    	}
    	
    	if(f1) continue;
    	
    	data.clear(); data.str(s1);
    	
    	for(int i = 1, x; i <= m; ++i)
    	{
    		data >> x;
     		use[x] = 1;
     		if(!mp[x][i]) return WA3();
    	}
    	
    	for(int i = 1; i <= n; ++i)
    	if(!use[i])
    		return WA2();
    	
    }
    return 1;
}

int main(int argc,char *argv[])
{
    fin.open(argv[1]);
    fout.open(argv[2]);
    fstd.open(argv[3]);
    fscore.open(argv[5]);
    freport.open(argv[6]);

    int score = atoi(argv[4]);
    fscore<<score*judge()<<endl;

    fin.close();
    fout.close();
    fstd.close();
    fscore.close();
    freport.close();
    return 0;
}

  • 2 k 2^k 进制数
    一道数学题/记搜题,注意高精度
    数学分析:
    由于最高位(假设为 L L )的限制,分两种情况讨论
    1.最高位为 0 0 ,则每一位的取值范围均为 [ 0 , 2 k 1 ] [0, 2^k - 1]
    总方案数为: i = 1 m i n ( 2 k 1 , L 1 ) C 2 k 1 i \sum_{i = 1}^{min(2^k-1, L-1)}C_{2^k - 1}^{i}
    解释:枚举位数,对应的方案数为从 [ 0 , 2 k 1 ] [0, 2^k - 1] 选出若干数(有且只有一中排列是单调递增的)
    2.最高位非 0 0 (为 r r ),则后面每一位的取值范围为 [ r + 1 , 2 k 1 ] [r + 1, 2^k - 1]
    依照上面的在累加到 a n s ans 里去就行了
    递推:
    考试的时候其实想到了记搜,但从最高位往后推,忘记了连续为0的情况,然后 G G GG .
    f [ i ] [ j ] f[i][j] 表示第 i i 位数值为 j j 的方案数
    f [ i ] [ j ] = k = j + 1 f [ i 1 ] [ k ] f[i][j] = \sum_{k = j + 1}^{取值上界}f[i-1][k]

  • 地图
    水题,跳过


9.27

今天这套题目偏数学,整体来说还算好。

  • 取火柴游戏
    经典的 n i m nim 游戏。由于每堆的 S G SG 函数即为这堆的火柴数,因此若当前火柴的异或和为 0 0 ,则先取必败
  • 实数数列
    远古时期的题目,真的是一道数学题。
    由于这个神奇的式子,不能直接递归解决,对此考虑一下未知数。
    a 2 = x , a n 1 = y a_2 = x, a_{n-1} = y ,同时推导 a m i d a_{mid} ,然后解一个二元一次方程即可。
    P S : PS: 务必注意精度问题,靠谱的方法是记录系数;别想着单边推, n = 50 n = 50 时系数连 l o n g l o n g long long 都暴力
  • 排序集合
    连续 k k 个数能产生 2 k 2^k 个子集
    于是一位一位地找(定位)即可
  • 平面上的最接近点对
    经典的分治题目
    二分点,递归处理。
    合并时,先取出 ( m i d . x a n s , m i d . x + a n s ) (mid.x - ans, mid.x+ans) 内的点,再按照 y y 轴排一遍序,用两点间的距离更新答案。枚举过程中根据 y y 轴的递增性利用当前答案来限制限制。
    关于排序的优化:其实可以用归并排序而不用快排。
  • 新汉诺塔
    只提供伪正解(洛谷上有 h a c k hack 数据)
    决策:从编号大的入手,这样能保证不会后面的移动产生影响。
    对于将 p p 号从 A A 移动到 B B ,先将小于 p p 的全部都移到 C C 去,递归处理即可。

9.28

都一把老骨头了,还被拉出去跑步,整个人都不好了。
这套题目真的是满满的心里阴影,让人质壁分离。

  • 加工生产调度
    有点印象,貌似是取最值排个序什么的,然后就蒙过了。
    贪心:证明不会,但还是可以感性地认知一下。一定要保证 B B 早开工,早完成。因此,将在 A A 厂加工时间短的放在前面,在 B B 厂加工时间短的放在后面。确定好顺序后,模拟一遍即可。
    技巧:对于这些不易证明但可以列出式子的贪心题,直接利用列出来的式子排序也行。
  • 集合划分
    成功地打脸自己,前几天自己才讲了第二类斯特林数,结果就我没有想到模型。
    题目模型:给定 n n 个不同的球, n n 个相同的箱子,允许空箱。求有多少种放法
  • 地毯填补
    分治经典例题。四分递归处理
  • 金币阵列问题
    数据有点毒瘤,第二个点就是过不了( n = 1 , m = 100 n = 1,m = 100 ),弃疗
    魔板系列的问题:
    明确一点,行与列是相互独立的。
    首先枚举起始矩阵的哪一列与目标矩阵第一列相匹配,然后通过行变换来保证匹配(行就此确定下来),通过搜索求出剩下的列相匹配的最小次数即可。
  • 雇佣计划
    这道题其实还算好想,但考场上直接跳过了。
    共有两种方法:标准贪心与无脑dp
    贪心:重点在于多余的人员是否要保留,对此,枚举保留的人数,暴力计算即可。

9.29

题目有点多,看着都头痛,不过整体难度不大。

  • 餐巾计划问题
    其实是会的,只不过是真的不想法这一题,于是跳过,于是最后就没时间了。
    正解为费用流三分+贪心
    My Blog
  • 二维表
    卡特兰数。然而数据并不友好,写了个高精加上递推的式子(考试的时候忘记组合数的那些公式了,尴尬)并不给分。
    公式: C a t n = C 2 n n n + 1 = C 2 n 2 C 2 n n 1 Cat_n = \frac{C_{2n}^n}{n + 1} = C_{2n}^2 - C_{2n}^{n - 1}
    推导过程:构造如下的 01 01 序列:若某个数放在第一行,则该位置上为 0 0 ,放在第二行则为 1 1 ,满足条件的 01 01 序列必定满足 0 0 的个数大于等于 1 1 的个数.
  • 神秘数字
    推导一下就可以得出要求顺序对(表示心累,前缀和推出来了,就是没想到顺序对)
    推导过程:
    令原序列为 A i A_i 则对于区间 [ l , r ] [l,r] 满足 A l + A l + 1 + + A r &gt; ( r l + 1 ) m A_l + A_{l + 1} + \cdots + A_r &gt; (r - l + 1) * m 移项后可得 ( A l m ) + ( A l + 1 m ) + + ( A r m ) &gt; 0 (A_l - m) + (A_{l + 1} - m) + \cdots + (A_r - m) &gt; 0 若将 ( A i m ) (A_i-m) 用前缀和 S i S_i 表示,则为 S r S l 1 &gt; 0 S l 1 &lt; S r S_r - S_{l - 1} &gt; 0 \to S_{l - 1} &lt; S_r 因此,只需要求出 ( A i m ) (A_i-m) 的前缀和后,求顺序对就好了(推荐归并)
    注意的是 S 0 S_0 也要算进去
  • 地标访问
    解法倒是挺多的。
    可以枚举+二分(其实可以利用前缀和以及数学的推导来优化二分的部分,直接定位),也可以类似滑动窗口直接扫过去(不过细节有点多)
  • 最多约数问题
    正解类似反素数,利用约数和定理,枚举 c i c^i ,然后再加一堆神剪枝(其实只要一个)
    由于数据十分水,网上各种玄学的算法都能过
    blog
  • 最大间隙问题
    排序题,最水没有之一

9.30

首先,还是远古数据的锅;其次,思路上还是有点紊乱,有些细节上没有注意到;最后,加了些不必要的玄学"优化",然后 G G GG

  • 邮局选址问题
    根据中点的性质,直接分别选取 x , y x,y 的中点即可。
  • 士兵站队问题
    本来是可以 A C AC 的,结果手抽加了一个不必要的玄学优化, W A WA 了几个点。
    对于y轴而言,选取中点即可。
    对于x轴而言,先将它们都出去各自的下标(从 1 1 开始),即处理成都到 x 1 x_1 的距离,然后求中点。(其实直接暴力也能过:贪心一点的想法,至少有一个士兵的 x x 是不会变的)
  • 数列极差问题
    数据有误。
    贪心题目。若每次去最大的两点出来,则最后的最小;若每次取最小的两点出来,则最后值最大。
    证:假设当前只有三个数 a , b , c a,b,c a b c a \leq b \leq c 一共有三种方案
    a , b , c a b c + c a,b,c \to a*b*c + c
    a , c , b a b c + b a,c,b \to a*b*c + b
    b , c , a a b c + a b,c,a \to a*b*c + a
    \dots
  • 信号增强装置问题/森林问题
    也可以说是一道贪心题,尴尬的是在细节方面处理错了(说好的双倍快乐呢)。
    从根开始处理,到了迫不得已要加信号增强装置的时候就装上。

10月

感觉太长了,还是另写一篇吧
点这里

猜你喜欢

转载自blog.csdn.net/qq_27121257/article/details/82759129