平邑集训(补题)
Day 1
A 咕咕
题目描述
解法
DP,设 表示从 走到 的方案数。
转移的时候,需要按照给定的限制走,如果一个点的(2)(3)限制冲突了,那么就标记一下,经过他的时候绕过他,时间复杂度 。
代码
点击查看代码
int n,m,t; int f[3001][3001]; int dp[3001][3001]; void solve() { cin>>n>>m>>t; int i,j; while(t--) { int a,b,c,d; cin>>a>>b>>c>>d; bool flag = 0; if(a==n&&b==m) continue; if((c==a+1&&d==b)||(d==b+1&&a==c)) flag = 1; if(!flag||f[a][b]==-1) { f[a][b] = -1; continue; } if(c==a+1) { if(f[a][b]==2) f[a][b] = -1; else f[a][b] = 1; } else if(d==b+1) { if(f[a][b]==1) f[a][b] = -1; else f[a][b] = 2; } } if(f[1][1]==-1) { cout<<0<<endl; return; } dp[1][1] = 1; for(i=1;i<=n;i++) for(j=1;j<=m;j++) { if(f[i][j]==-1) dp[i][j]=0; else { if(f[i-1][j]!=2) dp[i][j] += dp[i-1][j]; if(f[i][j-1]!=1) dp[i][j] += dp[i][j-1]; dp[i][j] %= mod; } } cout<<dp[n][m]<<endl; return; }
B 找子串
题目描述
解法
- Subtask 1
每次删掉 后重新暴力再找第一个 ,时间复杂度 。
- Subtask 2
每次删掉 后用 kmp 或哈希找到第一个 ,时间复杂度 。
- Subtask 3
每次删掉 后,从删掉位置之前的 个位置开始,用 kmp 或哈希找 ,时间复杂度 。
- 满分做法
用 kmp 从 中找 时,对于 中的每一个位置 ,记录以 为结尾的子串,和 最多匹配到哪里,可以用一个数组记录下来,比如 。
举个例子:若 .
则 ,因为 中的 g
与 中的 g
匹配。
则 ,因为 中的 go
与 中的 go
匹配。
则 ,因为 中的 g
与 中的 g
匹配。
则 ,因为 中的 go
与 中的 go
匹配。
则 ,因为 中的 goo
与 中的 goo
匹配。
则 ,因为 中的 good
与 中的 good
匹配。
在上面的例子中,如果将第一个 good
从 中删除后,没有必要从头开始找 good
,因为以前就知道 ,因此从 处继续 kmp 即可,可以通过栈来实现。
代码
点击查看代码
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 7; char s[maxn], t[maxn]; char sta[maxn]; int fail[maxn], fail1[maxn], n, m, top; void init() { for (int i = 2, j = 0; i <= m; i++) { while (j && t[i] != t[j + 1]) { j = fail[j]; } if (t[i] == t[j + 1]) j++; fail[i] = j; } } void kmp() { for (int i = 1, j = 0; i <= n; i++) { sta[++top] = s[i]; while (j && sta[top] != t[j + 1]) { j = fail[j]; } if (sta[top] == t[j + 1]) j++; fail1[top] = j; if (j == m) { top -= m; j = fail1[top]; } } } int main() { scanf("%s", s + 1); scanf("%s", t + 1); n = strlen(s + 1); m = strlen(t + 1); init(); kmp(); for (int i = 1; i <= top; i++) printf("%c", sta[i]); return 0; }
C LCS 问题
题目描述
思路
- 对于前 的数据
因为序列长度最多为 ,所以可以暴力 LCS。考虑 dp, 表示 数组前 个数和 数组前 个数的最长公共子序列。
如果 ,那么 。
然后 。
时间复杂度为 。
- 满分做法
关键在于保证 这 个数在 中分别出现 次。枚举 ,同时维护 表示 数组前 个数和 数组前 个数的最长公共子序列。同时要求这个最长公共子序列在 中必须以 结尾。
一开始对于每个 ,用二维数组存下数值 在 中的所有位置。枚举 时,枚举数值 在 中的所有位置 。这时候所有的 都是 数组中前 个数和 数组中前 个数的最长公共子序列。
我们令 ,那么 也就是 数组中前 个数和 数组中前 个数的最长公共子序列。
对于 的 ,显然 ,也就是说 不用改动。
那么我们现在就要单点修改,求前缀最大值,用树状数组维护 的前缀最大值,时间复杂度 。
代码
点击查看代码
inline int lowbit(int x) { return x&(-x); } inline void change(int x,int y) { for(int i=x;i<=N;i+=lowbit(i)) f[i]=max(f[i],y); return; } inline int query(int x) { int res=0; for(int i=x;i;i-=lowbit(i)) res=max(f[i],res); return res; } for(int i=1;i<=n5;i++) { int x=read(); c[x][++cnt[x]]=i; } for(int i=1;i<=n5;i++)b[i]=read(); for(int i=1;i<=n*5;i++) for(int j=5;j;j--) { int res=query(c[b[i]][j]-1); change(c[b[i]][j],res+1);
D 舞步
题目描述
Farmer John 的奶牛们正在炫耀她们的最新舞步!
最初,所有的 头奶牛()站成一行,奶牛 排在其中第 位。舞步序列给定为 ()个位置对 。在舞蹈的第 分钟,位置 与 上的奶牛交换位置。同样的 次交换在第 分钟发生,在第 分钟再次发生,以此类推,周期性地持续共 分钟()。换言之,
- 在第 分钟,位置 与 上的奶牛交换位置。
- 在第 分钟,位置 与 上的奶牛交换位置。
- ……
- 在第 分钟,位置 与 上的奶牛交换位置。
- 在第 分钟,位置 与 上的奶牛交换位置。
- 在第 分钟,位置 与 上的奶牛交换位置。
- 以此类推……
对于每头奶牛,求她在队伍中会占据的不同的位置数量。
注意:本题每个测试点的时间限制为默认限制的两倍。
输入格式
输入的第一行包含 、 和 。以下 行分别包含 ()。
输出格式
输出 行,第 行包含奶牛 可以到达的不同的位置数量。
提示
分钟之后,各个位置上的奶牛为 。
- 奶牛 可以到达位置 。
- 奶牛 可以到达位置 。
- 奶牛 可以到达位置 。
- 奶牛 可以到达位置 。
- 奶牛 可以到达位置 。
- 奶牛 从未移动,所以她没有离开过位置 。
测试点性质:
- 测试点 1-5 满足 。
- 测试点 6-10 满足 。
- 测试点 11-20 没有额外限制。
我们先考虑弱化版,即将 周期性地持续共 M 分钟
改为 无限循环
。
弱化版
考虑经过 分钟后,每头牛会途径某些点到达另一个点,假如 从 位置走到了 位置, 从 走到了 ,那么再经过一轮, 也会走到 位置。
也就是说 的运动轨迹会和 一样,只不过落后 分钟罢了。同时 经过的点也和 一样,所以像 一样的有相同运动轨迹的牛,它们的运动轨迹将会在 分钟之内构成一个环。
而环可以用并查集维护。接下来用 vector 记录每个点经过的点,对于一个环内的牛,用 set 统计每个点经过了多少个点,设为 ,则环内每一个牛能经过的点数也就是 。
时间复杂度 。
普通版
也就是多了个时间限制。
令 表示一个点开始走会完整走 轮, 表示走完 轮还需要走 步。
手动模拟一下,发现类似滑动窗口,每次暴力加入 步,计算答案,再删除当前点,把加入 步的点剩下的都加进去,时间复杂度为 。
注意 的情况和特判环大小 的情况。
(补)
Day 2
A 江桥的均衡区间
题目描述
解法
- Subtask 1 & Subtask 2
枚举 ,然后往后扩展,每次遇到一个数把他扔进去
- Subtask 3
枚举区间的起点终点,左右端点为
,移项,。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步