久违地发一次考试总结。因为这次写的比较详细,勉强能拿出来看看。
A
第一反应是线段树。(其实按位考虑说不定对于某些题也是一种突破口)
正解是连 \((2*p)-1\bmod n+1)\) 和 \((2*p+1)-1\bmod n+1\)
然后发现对于每个点的(虚)子树内,叶子节点的个数就够了。线段树内横向走编号是 +1
超级神秘结论好题。
B
max:一个套路是考虑最后一步。
但是我感觉也不好做 ba
对我的启示是可以手玩一下,把某种操作转成另外一种操作。
对于这个题,可以发现我们可以另一个点平移三格,然后发现可以用添加和平移操作来等效替代删除操作,然后平移就较为好统计?
可以把三个散点当成最后一步,然后区间 dp 中间的空隙。
C
博弈策略显然,充要条件也比较容易发现:不存在一对相邻 B 中间 S 个数的 log 向上取整大于后面 B 的个数。
然后可以容斥一下变成统计小 B 失败的方案数,这一步很有启发性,因为存在一个地方合法比较麻烦,但是任意地方都不合法是较为好维护的。
可以维护 cntB - log S,但是仔细一想发现比较唐,这种太猎奇的状态可能反而不好转移,所以可以改成直接记 cntB,然后转移一个后缀。
直接转移其实还是有点麻烦,可以考虑钦定这个位置 i 必须填 B,这样还方便统计答案。
感觉这个题要做出来需要很好的 dp 基本功,这正是我欠缺的。
D
也是一个比较考察 dp 基本功的题目。
是不是这种填表的题目用记忆化比较好写?
比较重要的信息有:现在是第几轮(c),干掉了几个人(j),当前在什么位置(i)。比较神奇的是,这个题还需要记录一个变量 l 表示想要统计答案的人在什么位置。
比较特别的是这个第四个状态维度,这启示我们对于状态相同但是答案不同的点,可以多记录一位来表示我们关注的点在什么位置。
还有一个需要注意的是,j 和 l 记录的都是相对于第一个人的位置,这是因为那些被干掉的人我们不关心,所以可以直接删掉。
想清楚了后似乎转移较为朴素。
还有一个启示是概率期望题要多手玩小数据来体会。
似乎有一个套路是如果不在乎轮数,可以认为每进行一次操作后,都循环移位一次,这样可以不记录 i 这一维。
E
牛逼题,详细写一下题解。这篇题解主要是对重开之星的题解的补充。
稍微转化一下后,可以认为题目在求:
如果是等于号,那就是我们熟悉的 CRT,但可惜不是。不过可以先分析一下等于号,找一下性质。毕竟只有三个限制说不定能有比较好看的形式呢?
首先上下相减后用裴蜀定理,容易得到:
(注:这里的 \(k\) 和上面的 \(k\) 毫无关系,只是因为我比较喜欢写成不定方程的形式)
如果枚举了合法的 \(b_1\),那么就可以快速计算出 \(b_2\) 和 \(b_3\),因为它们是周期性出现的。(周期是 \(\gcd(p_1,p_2)\) 和 \(\gcd(p_1,p_3)\))
发现若直接把 \(\operatorname{lcm}(\gcd(p_1,p_2),\gcd(p_1,p_3))\) 当成一个大周期,就会发现在每个大周期之间的 \(b_2\),\(b_3\) 出现次数是相同的。
为了方便表示,设 \(V=\operatorname{lcm}(\gcd(p_1,p_2),\gcd(p_1,p_3))\),若 \(p_1\neq V\),则可以递归到 \((r_1/V)\times f(V,V-1,p_2,r_2,p_3,r_3)\) 和 \(f(V, r_1\bmod V,p_2,r_2,p_3,r_3)\) 两个子问题上了。
对于 \(p_2\) 和 \(p_3\) 显然也可以进行这样的操作来递归子问题,递归三次之后就都满足了。画一个质因数的维恩图帮助理解:

相当于每次是保留了阴影部分,然后叉掉与另外两个无交的部分。

最后相当于就剩了个这个东西下来,可以感受到这玩意的性质肯定不一般,

标注一点字母方便表述:

也就是说可以设
终于出现这种乘法的形式了,不妨让 \(x\ge y\ge z\),因为 \(p_1\le 1e12\) 且 \(p_1 \ge xy\),这就是一个标准的根号分治的形式,所以 y 小于 \(1e6\),同理 z 小于 \(1e6\).
考虑 \(p_1\) 离 \(\operatorname{lcm}(p_1,p_2)\) 只差 \(z\) 倍,\(p_2\) 离 \(\operatorname{lcm}(p_1,p_2)\) 只差 \(y\) 倍,可以从这里入手。
可以维护两个区间 \([0,r_1]\) 和 \([0,r_2]\),第一个区间每次跳 \(p_1\) 个单位长度,第二个区间每次跳 \(p_2\) 个单位长度,每次区间的交就是对于 \(p_1,p_2\) 同时合法的区间,然后再 \(O(1)\) 计算这个区间对 \(p_3\) 的合法长度(这个是简单的)。对于这两个区间,每次找到末端靠前的那个跳,就能维护出所有对于 \(p_1,p_2\) 同时合法的区间,统计一下对于 \(p_3\) 的答案就做完了。

这个题给我们的一个很重要的启示是数论题可以通过画质因子的维恩图来辅助理解。
:::info[Click to see My Code]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 LL;
const int mod=998244353;
inline ll read() {ll x = 0; bool f(0); char ch = getchar();while(!isdigit(ch)) f = (ch == '-'), ch = getchar();while(isdigit(ch)) x = (x<<3) + (x<<1) + (ch^48), ch = getchar();return f ? -x : x;
}
inline void wt(ll x, bool md = 1) {//fl=1是换行,fl=0是空格if(x < 0) x = -x, putchar('-'); static short Stack[64], top(0);do Stack[++ top] = x % 10, x /= 10; while(x);while(top) putchar(Stack[top--] | 48);md ? putchar('\n') : putchar(' ');
}
#define For(i, s, e) for(int i = s; i <= e; ++i)
#define ifor(i, s, e) for(int i = s; i >= e; --i)
#define pb push_back
struct def{int x;inline operator ll() {return x;}inline def (ll val=0) : x(val) {} // ?inline def operator + (def v) {return {x + v.x >= mod ? x + v.x - mod : x + v.x};}inline def operator - (def v) {return {x - v.x < 0 ? x - v.x + mod : x - v.x};}inline def operator * (def v) {return {1ll * x * v.x % mod};}inline void operator +=(def v) {x = x + v.x >= mod ? x + v.x - mod : x + v.x;}inline void operator -=(def v) {x = x - v.x < 0 ? x - v.x + mod : x - v.x;}inline void operator *=(def v) {x = 1ll * x * v.x % mod;}
};
struct lim{LL p, r; void out() {wt(p, 0), wt(r);}};
LL gcd(LL a, LL b) {return b ? gcd(b, a % b) : a;}
LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
def get(LL x, lim tr) {return def((x / tr.p * (tr.r + 1) + min(tr.r, x % tr.p) + 1) % mod);} // mind these +1
def fun(lim a, lim b, lim c);
def calc(lim a, lim b, lim c) {def tmp;tmp = fun(a, b, c); if(tmp != -1) return tmp;tmp = fun(b, c, a); if(tmp != -1) return tmp;tmp = fun(c, a, b); if(tmp != -1) return tmp;if(a.p > b.p) {swap(a, b);}if(b.p > c.p) {swap(b, c);} if(a.p > c.p) {swap(a, c);} // LL g = gcd(gcd(a.p, b.p), c.p), y = gcd(a.p, c.p) / g, x = gcd(a.p, c.p) / g, z = gcd(b.p, c.p) / g; LL k = x * g; // 好像这些东西都用不到?只是用来分析复杂度的?ll s1 = 0, e1 = a.r, s2 = 0, e2 = b.r; def res;while(1) {if(s1 == s2 && s1 % c.p == 0 && s1) break; // ensure this is a periodll l = max(s1, s2), r = min(e1, e2);if(l <= r) res += get(r, c) - get(l-1, c);if(e1 < e2) s1 += a.p, e1 += a.p;else s2 += b.p, e2 += b.p;}return res * def(a.p * b.p / s1 * c.p % mod);
}
def fun(lim a, lim b, lim c) {LL v = lcm(gcd(a.p, b.p), gcd(a.p, c.p));if(a.p == v) return -1;return def(a.r / v % mod) * calc({v, v-1}, b, c) + calc({v, a.r % v}, b, c);
}
int main() {// freopen("file.in","r",stdin);// freopen("file.out","w",stdout);lim tr[3];For(i, 0, 2) {ll g = read(), r = read();tr[i].p = g + r, tr[i].r = g - 1;}wt(calc(tr[0], tr[1], tr[2]));return 0;
}
:::