4.2 校内模拟赛游记
次阅读
4 min read
其实正解都想得挺快的,但是 D 题细节有些恶心,最终调了很久,没空做最后一题了。
A
一个字符串,若干个操作,每次翻转或在开头/结尾插入,输出最后结果。
水题,deque
解决。
B
一个字符串,维护区间内不同的字符个数,支持单点修改。
线段树裸题。
C
水题,题目都不想打了。
D
随便来个样例,把所有的列出来。
3
35
354
3543
5
54
543
43
3
中间显然有很多重复。
若只维护第一组,那么每次每个要减去的数不一样,所以从后面开始。
3
43
543
3543
4
54
354
5
35
3
这样对于每一组,就是把第一个去掉,然后剩下的每一个减去最后一个位再除以十就可以了。
因为对于所有数的处理都一样,所以直接把取模完每一个答案有几个给记下来,然后维护一个 , 满足经过了哪些操作原来答案为 的答案会变为 , 然后加上这个就可以了。
具体地,比如有 个数,我现在要求倒数第二组的答案,则 需要满足( 是 的逆元)
化简一下就是每次 加上 。
然后你就可以交了。
代码
#include <cstdio>
#define int long long
int n, m, p, ans, f[10005];
char s[200005];
signed main() {
scanf("%lld%lld", &n, &p);
scanf("%s", s + 1);
for (int i = n, pow = 1, now = 0; i >= 1; i--, pow = pow * 10 % p) {
now = (now + (s[i] - '0' + p) * pow % p) % p;
f[now]++;
}
for (int i = n, pow = 1, now = 0; i >= 1; i--, pow = pow * 10 % p) {
ans = ans + f[now];
now = (now + (s[i] - '0') * pow % p) % p;
f[now]--;
}
printf("%lld", ans);
}
交了以后会发现:WA 了
细细一想,发现若 是 的约数时连个逆元都没有,你乘个锤子!
所以特判一下 是 和 的情况。
if (p == 2 || p == 5) {
for (int i = 1; i <= n; i++)
if ((s[i] - '0') % p == 0)
ans += i;
printf("%lld", ans);
return 0;
}
现在真的过了。这错误我调了 分钟
E
原题是 ABC158F。
看了题解 觉得 dp 还是比较显然的,肯定得排一个序,然后倒着。因为我们能唯一能确定的状态就是 这个编号,根本没有机器人,方案只有 。
表示后 个的方案数。显然可以 , 其中 是后面不受影响的。
问题在于后面那个怎么求。 next
即后面第一个大于 的,那不就是单调栈吗?
代码
#include <cstdio>
#include <stack>
#include <algorithm>
const int N = 200005, p = 998244353;
int n, f[N];
std::pair<int, int> a[N];
std::stack<std::pair<int, int> > st;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d%d", &a[i].first, &a[i].second);
std::sort(a+1, a+1+n);
f[n+1] = 1;
for(int i = n; i >= 1; i--) {
int t = a[i].first + a[i].second, next = i+1;
while(!st.empty() && t > a[st.top().first].first) {
next = st.top().second;
st.pop();
}
st.push({i, next});
f[i] = (f[i+1] + f[next]) % p;
}
printf("%d", f[1]);
}