重启计划

题单

不会也没关系,就当拓宽知识点。如果偶然看到这个帖子,我断更了,欢迎到发个邮件狠狠督促我,2463744614@qq.com。希望各位也好好坚持,每天好好学习,好好吃饭,好好休息,好好锻炼。

11.19

ABC 328 F 带权并查集板题

11.28

ABC 326 E 概率题

ABC 326 F 模拟+双向 dfs

ABC 325 E 最短路 两段

11.29

ABC 325 F 背包 dp

ABC 325 G 区间 dp

ABC 324 E 简单前缀和

11.30

ABC 324 F 二分答案
这道题有个约束$u< v $,所以没有环且边的方向都是从小到大,因此从 1 到 n 枚举已经满足拓扑性质,之后就二分答案遍历图判断。

ABC 323 E 完全背包

ABC 323 F 推箱子模拟题
唉,n多个判断最后还是错两个点,看大佬代码镜像+翻转减少判断情况感觉自己像个傻子。

12.1

ABC 322 E 六维 dp

ABC 322 F 线段树板子 维护前后缀

ABC 321 E
题意:对于一个n个节点的完全二叉树,问距离节点x为k的节点数量

12.2

ABC 321 F 背包 dp
背包 dp 元素没有前后顺序,+x 就正常放入,-x 就逆向取出

ABC E lcm
思维题 靠题感猜到用 lcm

ABC 318 E 水题
今天下午玩文明六玩疯了,下午两点半玩到6点,出去吃饭,再回来就是8点多了,玩到9点半洗澡,
洗完澡不想写了,找了这道水题凑数。。。

12.3

ABC 318 F

题意:
\(n\)个物品,每个物品位置为\(x_i\)
有一个机器有\(n\)条腿,当机器在位置\(K\),腿\(i\) 的范围为\([k-l_i,k+l_i]\)
求有多少个位置\(K\)能够拾取到所有物品?

看样例可以知道,答案是由多个连续段组成
对于符合条件的连续段 \([L,R]\) ,那么肯定有位置 L-1 不能拾取到所有物品

所以将每种情况 第 j 条腿拾取第 i 件物品 的上限 \(x_i+l_j\) 和不能拾取的上限 \(x_i-l_j-1\)放进数组排序后逐个位置判断是否可以拾取到 n 件物品,可以就加上贡献.

ABC 318 G 圆方树

涉及图论的知识都是很头疼,根本不会。

1.先将原图中 A 到 C 的路径提取出来

2.然后求点双连通分量,用类似 tarjan 求割点的方法求点双,存储每个点双包含的点,

3.判断。如果一个点双中有 b,那就可以。当然,如果提取出来的路径中原本就包含 b 节点,那也可以。

ABC 331 E 堆

12.4

cf 912 B

cf 912 C

倒过来 dp,对于当前 i 来说,要么加上后面的总和再加 a[i],要么直接+a[i]

ABC 317 E BFS

12.5

ABC 314 D

ABC 314 E 概率题

ABC 317 F 数位 dp

12.6

ABC 314 F 并查集

ABC 313 E 思维题
忘取模了,找问题 tm 找了半个小时

ABC 312 D 括号匹配 DP

设状态 f[i][j]为 前缀 i 中有 j 个左括号的合法方案,答案为 f[n][n/2]

12.7

ABC 311 D DFS

ABC 312 E
判断三维空间的每个长方体有多少个其他的长方体与它共享一个面。

ABC 312 F

先将三种物品分开排序,\(X_i\)记录为取 i 个不用开环器的罐头的最大贡献,\(Y_i\)记录为开环器和标准罐头的总和个数为 i 的最大贡献,最后枚举一下计算取 m 个物品的最大贡献。

12.8

ABC 311 E DP

ABC 311 F 看不懂的计数 dp

过两天考试了,要断了。

12.11

atcoder 的题和以前做的接轨了,接下来要做 codeforces 了。而且这两周大概只能做下水题了,要复习。

ABC 309 D 最短路

ABC 309 E

有个限制条件 父亲是比儿子小的,所以从小到大枚举满足拓扑序

cf 914 div2 A

12.12

CF 914 div2 B 双指针

CF 912 div2 C

m>=3 直接输出 0 。因为随便取两个数 a,b 连续操作两次 a-b,两个相同的数再操作一次就得到 0

m=1 相邻两项差
m=2 任意两项的差与 a 数组对比,双指针求差

注意最小值不一定在差中,也可能在原来的值

CF 913 div3 C

12.13

cf EC Round 519 B 贪心

CF 913 div3 D 区间交集问题

CF 913 div3 E

要满足 a+b+c=n 且三个数的数位和等于 n 的数位和,那就不能有进位

那么每一位都是互相独立的,对 n 的每一位 x,有 i+j+k = x, 将每一个位的满足条件的三元组个数相乘即为答案

12.17

codeforces 915 div2

C 思维题

找出最大的字符串子序列 t,它必然是单调下降的,在操作一次后,下一次的最大子序列一定是 t 减去了开头的子序列。

D 单调栈 求循环数组最大 mex 和

对于静态数组,mex 数组一定是一段一段的,最后的位置 mex 一定为 n,不考虑最后一个位置,对于段[L,R],这一段的 mex 为 x,则一定有 a[R+1]=x,且 a[L]为从 R 往前的第一个小于 a[R+1]的数。

因此用单调栈维护单调递增,对于位置 i,假设它有贡献,那它的贡献为\(a[i]*(i-最近的一个小于它的数的位置)\),对于栈中比它大的位置,要先把它们的贡献先消除再出栈。

循环数组,直接把数组复制一遍,大于 n 之后插入继续判断即可,对于前面的重复的贡献,在维护单调的过程中会自动删除。

12.18

ABC 333 D

如果 1 是叶子节点那就输出 1,否则以 1
为根节点统计子节点个数,答案为 n-节点 1 的最大子树的节点个数。

ABC 333 E

贪心从后面往前

ABC 333 F 概率 dp

每次遇到无限循环的概率题都不会做。。。
看了题解也不知道怎么做,之后系统学一下概率 dp 好了。

12.19

在 OiWiki 上学了几道容易上手的概率 dp,之后继续。

概率 dp 大概是有三种:简单 dp 套了个概率的皮
能写出状态转移但不满足无后效性需要变换公式或者转换方法的题
连状态转移都极其复杂的题。

cf 148D 概率 dp

Poj2096 概率 dp 求期望

cf 24D 概率 dp 高斯消元

12.20

洛谷 P4316 简单概率 dp

hdu 4576 简单概率 dp

zoj 3329 概率 dp 公式变换

题意:3 颗骰子,分别有 k1,k2,k3 个面,摇到 a,b,c(不能打乱顺序)就清空分数,否则加上点数和的分数,分数>n 时结束,求结束的扔骰子次数的期望

12.25

ABC 334 D 前缀和

ABC 334 E dfs

ABC 334 F 单调栈

设状态\(dp_i\)为送完前 i 个人礼物的最短路程

状态转移方程
对于\(i-k<j<i\)

\[dp_i = min(dp_j+d(0,j+1)+d(j+1,j+2)+...+d(i-1,i))+d(0,i) \]

此处\(d(i,j)\)表示第 i 个人到第 j 个人的直线距离,第 0 个人为老师。

用前缀和\(s_i\) 表示从老师按顺序走到第 i 个人的距离,状态转移方程就变为

\[dp_i = min(dp_j+d(0,j+1)+s_i-s_{j+1})+d(0,i) \]

显然,维护最小的\(dp_j+d(0,j+1)-s_{j+1}\) 由于最多携带 k 个礼物,所以要用单调栈。

12.26

ABC 334 G tarjan 求割点

去复习一下 tarjan 知识点

12.28

EC Round 160

A 前后缀判断一下

B 记录 0 和 1 的个数,思路就是根据原来的串反着放字母,放不了那么该位置就是最长的匹配了,要删的字母个数即为原本长度减去该位置


C 位运算简单题

D 单调栈+dp,具体思路可以看这个up 主的讲解

12.29

Codeforces 918 div 4

E

题意是找到一段连续的数组使得奇数位置的和等于偶数位置的和,用前缀和判断,奇数位是加,偶数位是减,输出 YES 的情况有前缀和某一位是 0,或者前缀和某两位相等

F 区间交问题

经典树状数组,先离散化,再以右端点 R 排序,再用树状数组插入左端点 L,每次判断[L,R]里有多少个点即可。

G

思路:设\(f_i\)为从 1 走到 i 的最短路程,可以这样想,从某点 j 开始,它走到 i 的路程全是骑第 j 辆单车,状态转移方程为:$$f_i = min(f_j+s_j* dis(i,j))$$,要求两点间最短距离,弗洛伊德会超时,那就每个点都弄一遍 dijkstra 得到每个点对的距离,最后优先队列从小到大更新值。

12.30

cf 917 div2 B

题意:每次操作可以删除串的第一个字母或第二个字母,问最多可以得到几个不同的串


思路:实际上每个串分成两部分,首部和后面,首部不同则字符串不同,遍历记录前面不同字母的个数,相当于 1~i 选个首部出来拼接后面的后缀。

cf 917 div2 C

贪心,当全部变 0 之后,不管怎么用几次操作 1 最多只有一次贡献,所以在第一次用了操作 2 后,后面一定是操作 1 和 2 轮着来,所以判断操作 2 第一次在哪用了就行。而第一次重置的贡献最多为 n,而第 2*n+1 天的贡献最多等于第一天就重置然后用多出的 2n 天来弄出 n 个贡献。所以第一次操作 2 最多在 2n。

12.31

cf 917 D 树状数组好题

确实很好,但对我来说难度太大。

2024.1.1

ACWing 168 周赛 C

思路:先处理出两条边能由哪些长度的边构成,然后对于两条边的每一种构成 x1,y1,x2,y2,其中 x 为横边长度,y 为竖边长度,判断$ \frac{x1}{y1} =\frac{y2}{x2}$,因为它要求每条边都不能水平或竖直,如果第三条边不满足这一条件,则 x1=x2 或者 y1=y2,哪个成立就把那个取反就好了。

ACWing 168 B

先处理出水平翻转和竖直翻转以及两者的叠加,这样加上原图就 4 个图,对于每个图旋转判断是否等于目标图即可。

Codeforces GoodBye 2023 D

好久没碰到打表题了,而且一直以来构造题都不是很会。

2024.1.2

ACwing 周赛 135 C

把订单数组和货车数组排个序,记录编号,每辆货车运送它能运的货中最大价值的那个
贪心的证明可以看 y 总的视频

Acwing 周赛 134 C

题意:一个 n 个点 m 条边的有向无环图,没有重边和自环,问能加多少条长度为 1 的边,加了之后依然满足

  1. 没有重边和自环;
  2. 点 s 和点 t 的最短距离不变。

思路:范围只有 1000,分别从 s 和 t 跑两遍最短路,那么两个点 a 和 b 能加边的条件为:

  1. a 和 b 不相邻。
  2. \(ds_a+dt_b+1\geq ds_t\)\(ds_b+dt_a+1\geq ds_t\),其中\(ds_a\)为 s 到 a 的最短距离,以此类推。

Acwing 周赛133 B

题意:
寻找aj与09的一一对应关系,使得n个字符串
根据映射关系变成正整数且它们的和为最小值,注意字符串在映射后的值不能有前导0

思路: 对每个字符串按照10进制的关系统计字母的个数,如 \(abcde\) 中a就有10000个,b有1000个,然后将数量多的字母分派小的数值,注意要标记哪些字母不能为前导0。

2024.1.3

cf GoodBye 2023 E 线段树

太难,直接看题解。

Acwing 4966 简单 dp

题意:一个 01 串,有些位置为'?',问怎么填使得不重叠的 00 或 11 数量最大,求最大数量

定义 f[i][j]是前 i 位,最后一位为 j 的最大数量

状态转移:
若当前位不参与贡献,则为 max(f[i-1][0],f[i-1][1])
当前位参与贡献的条件为前一位与当前位相同或者前一位为?,此时为 max(f[i-2][0],f[i-2][1])+1

Acwing 5410 区间覆盖问题

二分答案 排序

2024.1.6

牛客小白月赛 85 D

牛客小白月赛 E

Atcoder 335 A~D

E 和 F 想了很久都不会,明天再看题解

2024.1.7

Atcocder 335 E

题意:n 个点 m 条边的图,每个点有权值 a_i,
对于一条从 1 到 n 的路径,按照节点访问的顺序,如果是非递减序列,贡献则是不同的权值的个数
问从 1 到 n 的路径中,最大贡献值是多少

思路:非递减序列,那末端节点大于等于前一个节点,且要尽可能小
用优先队列实现

Atcoder 335 F

状态转移方程 f[i+a[i]x] +=f[i] i+a[i]x<=n,即位置 j 能从 i 转移过来的条件是:i|j(mod a[i])
采用分治,设置一个值 K,
当 a[i]>=K 时 直接用转移方程转移 n/K
当 a[i]< K 时,采用记忆化方法,p[i][j] 代表在模 i 条件下余 j 的贡献 p[a[i]][i%a[i]]

Acwing 周赛 137 B

题意:n 块蛋糕,k 头奶牛,确定一个数 x,x 不能大于 m,轮着分给每一头奶牛 x 块,轮完再从头开始分,问第一头奶牛分到的蛋糕最多为多少

2024.1.15

复盘一下昨晚的 AtCoder Beginner Contest 336

C

题意:一个数是好数的条件为它的数位全是偶数,问第 n 个好数为多少。

思路:按照 0,2,4,6,8,20,22...的规律,像五进制,将 n 减一后不断除以五并且把对应余数的位加到字符串上(因为实际上的结果可能大于 int_64),注意当 n 为 1 的情况。

D

题意:数组的修改操作有两种:一种为某个位置的数-1,另一种为删去数组首部或尾部的数,问是否能够将数组构造成{1,2,3,...,k,k-1,k-2,...,1}

思路: 经典前后缀问题,对应位置 i,以它为顶点的从左到右的上升连续数组为\(l_i\),其值为\(min(l_{i-1},a_i)\),从右到左同理,最后枚举每一位判断一下。

E

题意: 经典数位 dp 问题,问范围内有多少个数可被它的数位和整除。

可以看看HDU 4389的代码

F

题意 :一个 nm 的数组,操作为将一个(n-1,m-1)的数组旋转,问多少此操作能使\(a[i][j] = (i-1)*m+j\),超过 20 次就输出-1

思路:dfs 题,对于一个图(横纵坐标从 0 开始),它有四种变化方式,分别将左上角为(0,0),(0,1),(1,0),(1,1)的数组旋转,单从起点搜,搜到 20 步的时间复杂度为\(4^{20}\),会 t,所以分别从起点图和终点图搜到 10 步,时间降低到$4^{10} $,

2024.1.27 AtCoder Beginner Contest 338

C 枚举

题意:有 n 类材料,每种材料数量为\(q_i\),每份食物 A 花费材料 i 为\(a_i\),食物 B 花费材料 i 为\(b_i\),问最多能弄出多少食物?

思路:因为数据范围只有 1e6,且 n 最多为 10,所以暴力枚举食物 A 的数量,看剩下的材料能弄出多少份食物 B,两者相加取最值

D 差分

这题比 E 题还难,赛后想了半个小时才想出来。

题意: n 个点 n 条路形成一个圈,点 x 与点 x+1 相连,点 n 与点 1 相连,然后 给出 m 个点,问删除某条边后按顺序从\(X_1\)走到$X_m 的最小步数

思路:原本没有删边时,\(X_i\)\(X_{i+1}\)的最短距离为\(min(|X_i-X_{i+1}|,n-|X_i-X_{i+1}|)\),但删了一条边后可能不能走最短的距离,所以先把所有最短距离算进答案里,然后用差分数组记录删除边后对答案增加的贡献,\(d_i\)表示删除 i 到 i+1 的这条边的影响。

分类讨论:假设\(X_{i+1}>X_i\) ,令 x = \(|X_i-X_{i+1}|\) , y =n-x ,dec = abs(x-y)

  1. \(x=y\) 则删除任一条边都没影响
  2. \(x<y\) 则走的是从\(X_i\)\(X_{i+1}\)这条路,那么对于\(X_i\)\(X_{i+1}\)之间的边,删除它们意味着答案要增加 dec
  3. \(y>x\) 则走的是从\(X_{i+1}\) -> n -> 1 -> \(X_i\) 这条路

最后看看删除哪条边的影响最小加上去即可
代码

E

题意:2*n 个点围成一个圆,然后有 n 条线段,问是否有线段相交

思路:因为每个端点都不同,没有重复,将线段的端点分为左端点和右端点(左端点小于右端点,不满足就交换),左端点为 1,右端点为-1。显然,一条线段和其他线段不相交的条件为这一线段的两端点内的左右端点数相等。用数组记录位置 i 的左端点,枚举 1 到 2*n ,记录前缀和,若位置 i 有左端点且它们的前缀和不相等,则一定与某线段相交。

代码

2024.2.4 AtCoder Beginner Contest 339

D bfs

题意:两个玩家同步运动,遇到边界或者障碍不能前进,问两人走到同一格的最小步数,不能则-1.

思路:由于迷宫范围 n 最大为 60,所以直接用状态\(f[x1][y1][x2][y2]\)表示玩家 1 走到(x1,y1)同时玩家 2 走到(x2,y2)的最小步数,bfs 一遍就行了。

代码

E 区间求最值

题意:求一个最长的子序列,相邻元素间的绝对值不超过 D

思路:元素值限制在[1,\(10^5\)],设\(f_i\)为以 \(a_i\) 为结尾的子序列的最大长度,状态转移为\(f_i = max(f_k)+1,\),其中\(|a_i-a_k|\leq D\),用线段树求最大值,一边遍历一边更新即可。

代码

AtCoder Beginner Contest 344 F

思路:思路: 每次移动需要钱,假设在点(i,j),那么到达这点应该需要最少的操作 cnt 还能获得在 cnt 次下最多的钱,
设 dis[i][j][x][y] 为 从点(i,j)到点(x,y)的最少花费
。枚举起点和终点,复杂度为(\(n^4\))

int n,m,k;
int a[110][110],r[110][110],d[110][110];
int dis[110][110][110][110];
void solve()
{
	read();//读入数组
		for(int i=1;i<=n;i++) 
	{
		for(int j=1;j<=n;j++) 
		{
			vector dp(n+1,vector<ll>(n+1,inf));
			dp[i][j]=0;//dp[x][y] 为 从i,j到x,y的最小花费 
			for(int x=i;x<=n;x++) 
			{
				for(int y=j;y<=n;y++) 
				{
					if(x==i&&y==j) continue;
					dp[x][y]=min(dp[x][y],dp[x-1][y]+d[x-1][y]);//从上面到这点 
					dp[x][y]=min(dp[x][y],dp[x][y-1]+r[x][y-1]);//从左边到这点 
				}
			}
			
			for(int x=i;x<=n;x++) 
			{
				for(int y=j;y<=n;y++)
				{
					dis[i][j][x][y] = dp[x][y];
				}
			}
		}	
	}
	
	vector dp(n+1,vector<pii>(n+1,{inf,0}));//第一维记录操作次数,第二维记录到达还剩多少钱 
	
	dp[1][1] = {0,0};
	
	for(int i=1;i<=n;i++) 
	{
		for(int j=1;j<=n;j++) 
		{
			for(int x=1;x<=i;x++) 
			{
				for(int y=1;y<=j;y++) 
				{
					if(x==i&&y==j) continue;
					int need = dis[x][y][i][j];
					int now = dp[x][y].se;
					int k = (need - now+a[x][y]-1)/a[x][y];//需要等多少次才能够钱出发 
					if(k<0) k=0;
					int v= dp[x][y].fi+i-x+j-y+k;
					int val =  now+k*a[x][y]-need;//走完还剩多少钱 
					if(v<dp[i][j].fi) 
					{
						dp[i][j] = {v,val};
					}
					else if(v==dp[i][j].first&&val>dp[i][j].se)
					{
						dp[i][j] = {v,val};
					}
				}
			}
		}
	}
	cout<<dp[n][n].fi<<endl;
}

2024.3.23

动态求树的直径

题意

有一棵树,一开始有四个节点,每次操作会选择某个叶子节点添加两个子节点,每次操作输出树的直径

思路

用个数组st记录所有深度最大的节点,它们中的一个或两个肯定是某条直径的一端,对于每次操作选择的节点,如果它在st中,那新的两个叶子节点就变为深度最大的节点,且直径长度+1;如果不是,那么求新的叶子节点到任意一个st中的节点距离相等(想一想为什么),这个距离与原来的直径相比取最大值,距离用LCA求

//2024.3.23
int n,m,k;
int h[N*2],ne[N*2],e[N*2],idx;
int dis[N*2],f[N][20],vis[N];

void add(int a,int b) 
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}

int LCA(int x,int y) //LCA求最近公共祖先
{
	if(dis[x]<dis[y]) swap(x,y);
	
	for(int i=19;i>=0;i--) 
	{
		if(dis[f[x][i]]>=dis[y]) x=f[x][i];
	}
	if(x==y) return x;
	for(int i=19;i>=0;i--) 
	{
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	}
	return f[x][0];
}

void work(int x,int y) //处理增加的节点
{
	add(x,y);
	f[y][0]=x;
	for(int i=1;i<20;i++) 
	{
		f[y][i]=f[f[y][i-1]][i-1];
	}
	dis[y]=dis[x]+1;
}

void solve() 
{	
	memset(h,-1,sizeof(h));
	cin>>n;
	int cnt=4,res=2;//一开始的直径就是2
	vector<int> st;
	work(1,2);
	work(1,3);
	work(1,4);
	vis[2]=vis[3]=vis[4]=1;
	st.push_back(2);
	st.push_back(3);
	st.push_back(4); 
	while(n--) 
	{
		cin>>m;
		work(m,++cnt);
		work(m,++cnt);
		
		if(vis[m])//如果在st里,那直径+1,且新的两个叶子节点深度最大
		{	
			vis[cnt]=1;
			vis[cnt-1]=1;
			res++;
			for(auto x:st) vis[x]=0;
			st.clear();
			st.push_back(cnt);
			st.push_back(cnt-1);
		}
		else 
		{
			int fa = LCA(cnt,st.back());
			res=max(res,dis[cnt]+dis[st.back()]-2*dis[fa]);
			if(dis[cnt]==dis[st.back()]) vis[cnt]=1,vis[cnt-1]=1,st.push_back(cnt),st.push_back(cnt-1);
		}
		cout<<res<<endl;
 	}	
}

Atcoder Beginner Contest 346 D

题意

给定一个01串S,每个位置$\ i $翻转的代价为 $\ C_i $, 求让S串满足,只有一个位置 \(i\) 使得 \(S_i=S_{i-1}\),求最小代价

思路

定义三维数组 \(f_{ijk}\)表示前i个位置,第i个位置字符为j,且前面是否存在连续相同的两个字符,

状态转移 ,令\(x=s[i]\)\(y=1-s[i]\):

\[\begin{cases} f[i][x][0] = f[i-1][y][0] \\ f[i][x][1] = min(f[i-1][x][0],f[i-1][y][1]) \\ f[i][y][0] = f[i-1][x][0]+c[i] \\ f[i][y][1] = min(f[i-1][y][0],f[i-1][x][1])+c[i] \end{cases} \]

代码

void solve() 
{
	cin>>n;
	string s;
	cin>>s;
	s=" "+s;
	for(int i=1;i<=n;i++) cin>>c[i];
	for(int i=0;i<=n;i++) 
	{
		f[i][0][0]=inf;
		f[i][0][1]=inf;
		f[i][1][0]=inf;
		f[i][1][1]=inf;
	}
	f[0][0][0]=0;
	f[0][1][0]=0;
	for(int i=1;i<=n;i++) 
	{
		if(i==1) 
		{	
			int x=s[i]-'0';
			f[i][x][0] = 0;
			f[i][x^1][0] = c[i];
		}
		else 
		{
			int x = s[i]-'0';
			f[i][x][0] = f[i-1][x^1][0];//末尾两个不同 
			f[i][x][1] = min(f[i-1][x][0],f[i-1][x^1][1]);//末尾两个相同 或不同 
			f[i][x^1][0] =f[i-1][x][0]+c[i];
			f[i][x^1][1] =min(f[i-1][x^1][0],f[i-1][x][1])+c[i];
		}
	}
	cout<<min(f[n][1][1],f[n][0][1])<<endl;
	
}
posted @ 2023-11-20 09:19  Liang2003  阅读(42)  评论(0编辑  收藏  举报