ICPC 2024 杭州站 打星游记

_luanyi_

2024-11-11 19:09:36

Life & Travel

ICPC 2024 杭州站 打星游记

其他两位队员都懒得写,那就我来写吧。

Day ??

正在信友队训练。被告知说信友队赞助了杭州站,送了几个名额,我们可能可以打星参赛。

感觉挺新奇的,有些兴奋,毕竟以前从未参加过 XCPC 呢。

Day ?

正式的通知下来了,信友队有四支队,名字是祖传的 学军信友队 [1-4] 队。

剩下三支队名额刚好分给我们机房一共九个人。 我在 $2$ 队,队友分别是 APIO Ag 爷 [dspt](https://www.luogu.com.cn/user/282929) 和 NOI Ag 爷 [szh](https://www.luogu.com.cn/user/290784),瑟瑟发抖,可以想象到被带飞的场景。估计只能端茶倒水了。 $3$ 队是传奇丁真王 [xhgua](https://www.luogu.com.cn/user/410239) 和机房唯二勾八 [lsj2009](https://www.luogu.com.cn/user/468657) 和 jiangly 粉丝 [Misty7](https://www.luogu.com.cn/user/344543)。 $4$ 队是机房小南梁 [yshpdyt](https://www.luogu.com.cn/user/229008) 和机房奶龙 [lycc](https://www.luogu.com.cn/user/486675) 和山东pc王 [王哥](https://www.luogu.com.cn/user/374987)。 在正赛前 $3,4$ 队都进行了一至若干场的 XCPC 场次的模拟训练,并且都发挥出了不错的实力。 但我们队从未进行过任何一场训练,一直都是自己管自己写自己的题,摆到正赛前。 我一直打算开场就默写码头,但有人说我码头写完比赛就结束了。 ## Day 0 11 月 9 日。 发现身份证落在家里了,急急急。 我们计划下午去打个热身赛就润,但上午甚至还有模拟赛。 于是模拟赛就咕掉了。 准备中午的时候三支队集合后一起坐地铁去杭师大,但是传奇 yshpdyt 一觉睡到了中午,差点迟到。 到杭师大就急急急找到位置坐下了。 第一次打 XCPC,就连热身看起来都十分新奇。 从未见过一个挂着气球有大屏幕的考场。 然后热身赛开始了。大屏幕上出现了 Judge Queue,很神奇,很好玩。 发现是全英文题面,寄。 > 注:热身赛我们并没有带字典。因为 dspt 曾经在初中里获得了 spelling bee(拼词大赛)的冠军,获得了 walking dictionary 的头衔。因此,他认为如果热身赛的题目他看得懂,那么甚至正赛也无需携带字典,他会翻译所有题目。 一共是四道题,我们队当时并没有什么策略,但由于我写题需要码头,因此由我先上机默写我的码头。 结果码头还没写完 dspt 就已经会了 A,换人,过题。 这时我下来看 C,发现这个题似曾相识,于是和 szh 讨论,于是他会了,换人。 szh 写完交了一发,没过,很神秘。 此时广播里在播报内容,但是很糊,还是全英文,难度严格大于英语听力。 幸好 dspt 擅长英语,翻译说题目顺序有问题,于是交到了正确的题目上,过题。 此时我开了 B,有一些初步的想法,和 dspt 和 szh 讨论,szh 又会了,遂写之,没过。 他发现有 corner case 没判,写之,没过。 此时我和 dspt 在看 D,但是我没看懂题目,幸亏 dspt 的翻译。 发现只要用字典树随便做一下就好了,又听 dspt 听广播说精度 checker 好像挂了,因此换我写 D,没过。 此时 B 重测完了,还是没过。 开始瞪眼,szh 发现他 B 做法假的妈妈都不认识了,稍微一改,过了。 szh 又发现我的 D 没有判前缀,改改改,过了。 罚时上天,严格低于 $3$ 队,感觉输完了。 之后开始在赛场上随机游走,随机面基了一些人,包括但不限于 [\_jimmywang\_](https://www.luogu.com.cn/user/90706) 和 [SalomeJLQ](https://www.luogu.com.cn/user/246979),都是高二考上西湖大学。 还获得了 Hydro 挂件,好看捏。 之后尝试在杭师大游走找地方吃饭,失败了。 碰到了随机刷新的 [猫猬兽](https://www.luogu.com.cn/user/87318)。 晚上就回信友队了。 感谢我的家长把我的身份证送来了。 十一点多就睡了。 ## Day 1 早上七点被闹醒,根本起不来啊。 赶到了信友队集合出发,有的人还是不出意外地迟到了。 马不停蹄地赶去了杭师大,找到位置后安稳坐下。 发现座位上有吧唧和明信片,好看捏,喜欢。 坐下后困意席卷而来,但是就要开赛了,只敢趴一会。 在赛前做了一些准备工作,我和 szh 互相确认了电子产品没有带入考场,拿了身份证。 **之后** dspt 从家里过来了,他拿了一本字典,我笑了。 开赛前 dspt 意识到没有拿身份证进考场,于是去包里拿了。 为了区分矿泉水瓶,dspt 把标签纸撕下来了之后上下倒过来贴,我把标签纸撕下来了之后正反反过来贴。 开赛前,我们一致了我们的目标,尝试拿金牌。 --- 下发了试卷袋,很新奇啊,第一次看到 OI 比赛使用试卷袋装的! 九点,开赛了,我把试卷袋优雅地拆开,取出试题。 想先去默写码头,发现机子还不能操作,于是只能读题了。 不知道先读什么,于是翻阅试卷,看到了 M,发现应该是一个不难的题。 还没读完就说机子可以用了,遂上机,先默个码头。 默完了,此时 dspt 已经会 A 了,遂写之,过题,激励了我们的士气。 szh 会了 K 之后貌似一直在看 C,当他说他会了的时候我两眼震惊。 我下机后就一直在想 M,感觉应该是一个可做题。 dspt 下机后 szh 开始写 K,遂取之。 因为机位空着,szh 开始写 C,我将我的 M 讲给 dspt,dspt 一会后就有了一个大约 $O(n\sqrt V)$ 的做法,但是是过不了的。 dspt 继续思考 M,而我取去跟榜,发现目前可做题有 E,H 等,遂读题,开始想 E,想到一个做法,结果假的妈妈都不认识了,又想了一个做法,觉得全都是道理。 突然有人叫住了 dspt:“手环给我。”原来是 dspt 忘记把运动手环放在场外了,心里一紧,这下要 dq 了。 幸好貌似并无大碍,继续做题。 dspt 发现貌似可以原地变成 $O(\sqrt V+n\tau(V))$,于是勒令 szh 下机,dspt 开写 M。 我们一直认为计算几何应当最后写,因此我喂给 szh 了 E 的题意。他思索一下发现没什么头绪,我喂给了他我的做法,他觉得假的妈妈都不认识了,反手就把我叉了。 于是继续想,szh 发现我的做法只要稍微改一改就全都是道理,我觉得很有道理。 此时 dspt 写完了 M,还在调,我喂给了 szh H 的题意。 dspt 调完了,很担心自己的常数,交,喜提一发罚时,竟然是 T。 dspt 把 map 改成了 unordered_map,交,又是 T。 dspt 又改了一改,还是 T。 dspt 觉得很神秘,快速写了个拍以后下了机,换我写 E,他和 szh 讨论 H。 我码码码,写完之后挂的妈妈都不认识了,发现我排序了个寂寞,改改改,改了一会过样例了,交,**过了**。 此时 szh 已经会了 H,还会了之前开的 F,上机写代码,屏幕分成两半,左边 dspt 使用瞪眼法调题。 dspt 还开了 G,和 szh 讨论了一下感觉应该是一个很码量的题,遂弃。 我选择跟榜开题,选择了 B,发现是一个 ds,读完题瞬间会了双 $\log$ 做法。 $n$ 是 $1e6$,经验丰富的 dspt 告诉我 $1e6$ 过双 $\log$ 做梦去吧。 也对,如果双 $\log$ 能过,现在怎么会只过了这么点。 开始精心思考怎么优化。此时 dspt 突然红温,发现他所谓的常数问题竟然是,多册没清空! 清之,过了。 szh 继续写,写完了,交,过了,开始写 F。 dspt 开了 L,有一些神秘的想法,但是并没有进一步的成果。 我和他讲了 B,他觉得这道题看起来就很可做。 过了一会,dspt 想到了 B 的单 $\log$ 做法,很厉害! 此时 dspt 问 szh 有没有理解错 F 的题意,于是 szh 成功发现了自己没有理解到强制在线,做法错的妈妈都不认识了。换人,dspt 开写 B。 szh 继续想 F 怎么强制在线,我跟榜,发现 I,J,L 都是当前的热门题,不会交互,不会像 L 这样的牛逼题。J 看起来是一个很神秘的状压计数,遂读题。 dspt 码码码,szh 还在想,我读了两遍 J 却读不懂题意,再读。 szh 会了 F 的强制在线,竟然直接从离线的根号变成了单 $\log$,太强大了。 我读了 $\inf$ 遍 J,终于读懂了是要可重集计数,就是说出题人能不能下次给一个形式化题意呢。 把题意喂给了 szh,szh 有所想法,想了一会,发现只要枚举 $\mathbb C$ 在 $\mathbb M$ 上高维前缀和搞一搞就可以了!太强了! dspt 过样例了,交一发,没过。 此时还有一个半小时,就快要封榜了,而我们还在银牌区,只有 $5$ 题,距离金牌还有点远。 我们加油打气不要急,我们手上藏了题! 意识到可以打印代码后打印了 B 的代码,打印了 B 的代码,迅速换 szh 写 F。 我和 dspt 一起丁真他的 B,他发现,他传奇般没有 push_down! 改之,直接过了。 szh 还在码码码,我和 dspt 开了 I,想了一会感觉没什么头绪,只知道要二分,但不知道怎么二分。 此时封榜了,心里有点紧张,现在的一切都是未知。 szh 写完了,交了一发,没过。 他迅速意识到有 corner case 没判,改改改,没过。 我和 dspt 看他的代码,为啥 tarjan 里有并查集啊?这确定对吗? 他说,无向图的时候确实是对的。 真厉害啊。 换我上机,帮他写了一个正常的 tarjan,他改了改码风后直接过了。 听到了吗,NOI Ag 不会写 tarjan。 此时还有半个小时。 我们现在 $7$ 题了,根据分榜前的排名是金牌,但是封榜后就不好说了,很多 $6$ 题队都在厚积薄发,而我们拥有者超级罚时。 szh 开始写 J,他觉得估计会寄,我们鼓励他,写得完最好,写不完问题也不大。 我和 dspt 看 I,还是没什么头绪。 szh 码码码,在 $288$ 分钟的时候写完了,样例一发过。 提交,直接过了! $8$ 题了,我看了看榜,稳金啊! 最后十几分钟,szh 懒得写他的 C 了。没人动电脑,我们稍微聊了一会天,总结了一下今天的表现。 战神(szh,最后关头过 J 一战封神)+ 战犯(dspt,M 三发 B 一发)+ 逃犯(我,contribution=1)= Au --- 出场后碰到了一些已经毕业的学长,稍微聊了聊。 $1$ 队差点就阿克了,$3$ 队和 $4$ 队貌似都坠机了。 火之剑圣下西洋、轰炸机数学学院、hzjsxxy、国际信息学奥赛金牌教练、NOI 钻石教师、杭州学军中学正高级教师、杭州学军中学拔尖创新中心主任、信友队科技培训有限公司创始人、学军中学信友队 $1$ 队 coach、学军中学信友队 $2$ 队 coach、学军中学信友队 $3$ 队 coach、学军中学信友队 $4$ 队 coach、徐先友老师为我们队在赛场门口进行了合影。 意识到可以把印有队名的挡板带走时,遂回赛场取之。 结果 $3$ 队 $4$ 队和 dspt 都回去了,只剩下我和 szh 去听讲评。 发现 I 也不是很困难,但确实时间很珍贵,交互做不来。 之后看了滚榜,还是比较刺激的。 最终获得了总榜 $\text{rk}25$,打星榜 $\text{rk}10$,初高中打星榜 $\text{rk}7$。 只有前五有实体牌,还是太菜了啊。 结束后,szh 就回家了,准备出发去 bnds 参加 CTT 集训了。 dspt 回家了。 我在和 \_jimmywang\_ 等面基,吃了晚饭后就回信友队了。 --- 附:我的考场版码头(2024 年版) ```cpp #include <bits/stdc++.h> #define fq(i,a,b) for (int i = (a); i <= (b); i++) #define fnq(i,a,b) for (int i = (a); i < (b); i++) #define nfq(i,a,b) for (int i = (a); i >= (b); i--) #define nfnq(i,a,b) for (int i = (a); i > (b); i--) using namespace std; #define int ll typedef long long ll; inline int rd () { int f = 1; char ch = getchar (); while (!isdigit (ch)) (ch == '-' ? (f = -1) : 0), ch = getchar (); int num = 0; while (isdigit (ch)) num = num * 10 + ch - '0', ch = getchar (); return num * f; } #define d rd () signed main () { return 0; } ``` 附:我的平时版码头(2024.11.6 版) ```cpp // last update: 2024/11/06 //#pragma GCC optimize(2) #include <bits/stdc++.h> #define fq(i,a,b) for (int i = (a); i <= (b); i++) #define fnq(i,a,b) for (int i = (a); i < (b); i++) #define nfq(i,a,b) for (int i = (a); i >= (b); i--) #define nfnq(i,a,b) for (int i = (a); i > (b); i--) #define fqs(i,a,b,c) for (int i = (a); i <= (b); i += (c)) #define fnqs(i,a,b,c) for (int i = (a); i < (b); i += (c)) #define nfqs(i,a,b,c) for (int i = (a); i >= (b); i -= (c)) #define nfnqs(i,a,b,c) for (int i = (a); i > (b); i -= (c)) #define elif else if #define McasesT int T = d; while (T--) #define Mcases(T) int T = d; while (T--) #define fi first #define se second #define pb push_back #define eb emplace_back #define pii pair <int, int> #define all(v) v.begin (), v.end () #define vi vector <int> #define vpii vector <pii> using namespace std; #define int ll typedef long long ll; typedef unsigned uint; typedef unsigned long long ull; typedef signed i32; typedef double db; typedef long double ld; //#define FileIO #if defined(FileIO) const string FileIOName = "1"; int FILEIO (string IN, string OUT) { try { freopen ((FileIOName + IN).c_str (), "r", stdin); freopen ((FileIOName + OUT).c_str (), "w", stdout); return 0; } catch (int) { return -1; } } int freFILEIO = FILEIO (".in", ".out"); #endif inline int rd () { int f = 1; char ch = getchar (); while (!isdigit (ch)) (ch == '-' ? (f = -1) : 0), ch = getchar (); int num = 0; while (isdigit (ch)) num = num * 10 + ch - '0', ch = getchar (); return num * f; } #define d rd () inline int rd (const int modp) { int f = 1; char ch = getchar (); while (!isdigit (ch)) (ch == '-' ? (f = -1) : 0), ch = getchar (); int num = 0; while (isdigit (ch)) num = (num * 10 + ch - '0') % modp, ch = getchar (); return (num * f % modp + modp) % modp; } namespace lay { struct Edge { int v, nxt; Edge () {} Edge (int _v, int _nxt) {v = _v, nxt = _nxt;} }; template <int VERTEXES, int EDGES, int initecnt = 0> struct Graph { Edge edge[EDGES]; int head[VERTEXES], ecnt; void addedge (int u, int v) {edge[++ecnt] = Edge (v, head[u]); head[u] = ecnt;} void addEdge (int u, int v) {addedge (u, v), addedge (v, u);} void init () {memset (head, 0, sizeof head); ecnt = initecnt;} Graph () {init ();} }; template <typename WT> struct EdgeW { int v, nxt; WT w; EdgeW () {} EdgeW (int _v, WT _w, int _nxt) {v = _v, w = _w, nxt = _nxt;} }; template <int VERTEXES, int EDGES, typename WT = int, int initecnt = 0> struct GraphW { EdgeW <WT> edge[EDGES]; int head[VERTEXES], ecnt; void addedge (int u, int v, WT w) {edge[++ecnt] = EdgeW <WT> (v, w, head[u]); head[u] = ecnt;} void addEdge (int u, int v, WT w) {addedge (u, v, w), addedge (v, u, w);} void init () {memset (head, 0, sizeof head); ecnt = initecnt;} GraphW () {init ();} }; template <int VERTEXES, int EDGES, typename WT = int> struct GraphFW : public GraphW <VERTEXES, EDGES, WT, 1> { void addEdge (int u, int v, WT w) {this -> addedge (u, v, w), this -> addedge (v, u, 0);} }; template <typename WT1, typename WT2> struct EdgeWC { int v, nxt; WT1 w; WT2 c; EdgeWC () {} EdgeWC (int _v, WT1 _w, WT2 _c, int _nxt) {v = _v, w = _w, c = _c, nxt = _nxt;} }; template <int VERTEXES, int EDGES, typename WT1 = int, typename WT2 = int, int initecnt = 0> struct GraphWC { EdgeWC <WT1, WT2> edge[EDGES]; int head[VERTEXES], ecnt; void addedge (int u, int v, WT1 w, WT2 c) {edge[++ecnt] = EdgeWC <WT1, WT2> (v, w, c, head[u]); head[u] = ecnt;} void addEdge (int u, int v, WT1 w, WT2 c) {addedge (u, v, w, c), addedge (v, u, w, c);} void init () {memset (head, 0, sizeof head); ecnt = initecnt;} GraphWC () {init ();} }; template <int VERTEXES, int EDGES, typename WT1 = int, typename WT2 = int> struct GraphFWC : public GraphWC <VERTEXES, EDGES, WT1, WT2, 1> { void addEdge (int u, int v, WT1 w, WT2 c) {this -> addedge (u, v, w, c), this -> addedge (v, u, 0, -c);} }; #define fe(G,u) for (int i = G.head[u], v; v = G.edge[i].v, i; i = G.edge[i].nxt) #define feh(G,u,h) for (int i = h[u], v; v = G.edge[i].v, i; i = G.edge[i].nxt) #define few(G,u) for (auto [i, v, w] = make_tuple (G.head[u], int(), int()); v = G.edge[i].v, w = G.edge[i].w, i; i = G.edge[i].nxt) #define fewt(G,u,T) for (auto [i, v, w] = make_tuple (G.head[u], int(), T()); v = G.edge[i].v, w = G.edge[i].w, i; i = G.edge[i].nxt) #define fewh(G,u,h) for (auto [i, v, w] = make_tuple (h[u], int(), int()); v = G.edge[i].v, w = G.edge[i].w, i; i = G.edge[i].nxt) #define fewht(G,u,h,T) for (auto [i, v, w] = make_tuple (h[u], int(), T()); v = G.edge[i].v, w = G.edge[i].w, i; i = G.edge[i].nxt) #define fewc(G,u) for (auto [i, v, w, c] = make_tuple (G.head[u], int(), int(), int()); v = G.edge[i].v, w = G.edge[i].w, c = G.edge[i].c, i; i = G.edge[i].nxt) #define fewct(G,u,T1,T2) for (auto [i, v, w, c] = make_tuple (G.head[u], int(), T1(), T2()); v = G.edge[i].v, w = G.edge[i].w, c = G.edge[i].c, i; i = G.edge[i].nxt) #define fewch(G,u,h) for (auto [i, v, w, c] = make_tuple (h[u], int(), int(), int()); v = G.edge[i].v, w = G.edge[i].w, c = G.edge[i].c, i; i = G.edge[i].nxt) #define fewcht(G,u,h,T1,T2) for (auto [i, v, w, c] = make_tuple (h[u], int(), T1(), T2()); v = G.edge[i].v, w = G.edge[i].w, c = G.edge[i].c, i; i = G.edge[i].nxt) } namespace lay { class cpx { public: double a, b; cpx () {a = 0, b = 0;} cpx (double _a) {a = _a, b = 0;} cpx (double _a, double _b) {a = _a, b = _b;} friend cpx operator + (cpx a, cpx b) {return cpx (a.a + b.a, a.b + b.b);} friend cpx operator - (cpx a, cpx b) {return cpx (a.a - b.a, a.b - b.b);} friend cpx operator * (cpx a, cpx b) {return cpx (a.a * b.a - a.b * b.b, a.b * b.a + a.a * b.b);} friend cpx operator / (cpx a, cpx b) {return cpx ((a.a * b.a + a.b * b.b) / (b.b * b.b + b.a * b.a), (a.b * b.a - a.a * b.b) / (b.b * b.b + b.a * b.a));} friend cpx& operator += (cpx &a, cpx b) {return a = a + b;} friend cpx& operator -= (cpx &a, cpx b) {return a = a - b;} friend cpx& operator *= (cpx &a, cpx b) {return a = a * b;} friend cpx& operator /= (cpx &a, cpx b) {return a = a / b;} }; } template <typename T> inline void Write (T x) { if (x < 0) putchar ('-'), x *= -1; if (x >= 10) Write (x / 10); putchar (x % 10 + '0'); } template <typename T> void write (char sep, char end, T x) {Write (x); putchar (end);} template <typename T, typename... Ts> void write (char sep, char end, T x, Ts... xs) {Write (x); putchar (sep); write (sep, end, xs...);} template <typename... Ts> void output (Ts... xs) {write (' ', '\n', xs...);} namespace lay { template <typename T = int> class _Math { public: static T exmax (T x) {return x;} template <typename... Ts> static T exmax (T x, Ts... xs) {return max (x, exmax (xs...));} static T exmin (T x) {return x;} template <typename... Ts> static T exmin (T x, Ts... xs) {return min (x, exmin (xs...));} template <typename... Ts> static T exgmax (T &x, Ts... xs) {return x = exmax (x, xs...);} template <typename... Ts> static T exgmin (T &x, Ts... xs) {return x = exmin (x, xs...);} static T gcd (T a, T b) {return !b ? a : gcd (b, a % b);} static T lcm (T a, T b) {return a / gcd (a, b) * b;} static T exgcd (T a, T b, T& x, T& y) { if (!b) {x = 1; y = 0; return a;} T ans = exgcd (b, a % b, y, x); y -= a / b * x; return ans; } static T crt (size_t n, T *a, T *p) { T M = 1, ans = 0; fq (i, 1, n) M *= p[i]; fq (i, 1, n) { T m = M / p[i]; T x, y; exgcd (m, p[i], x, y); ans += (x < 0 ? x + p[i] : x) * a[i] * m; } return ans % M; } static T excrt (size_t n, T *a, T *b) { T M = b[1], ans = a[1]; fq (i, 2, n) { T aa = M, c = (a[i] - ans % b[i] + b[i]) % b[i]; T x, y; T gcd = exgcd (aa, b[i], x, y); if (c % gcd) return -1; T bg = b[i] / gcd; x = c / gcd * x % bg; ans += x * M; M *= bg; ans = (ans % M + M) % M; } return (ans % M + M) % M; } static T power (T a, T b, T p) { T c = 1; while (b) { if (b & 1) c = c * a % p; a = a * a % p; b >>= 1; } return c; } private: static T C (T n, T m, T p) { T ans = 1; fq (i, n - m + 1, n) ans = ans * i % p; fq (i, 2, m) ans = ans * power (i, p - 2, p) % p; return ans; } public: static T lucas (T n, T m, T p) { if (!m || !n) return 1; return lucas (n / p, m / p, p) * C (n % p, m % p, p) % p; } static T bsgs (T a, T b, T p) { T mul = 1, t = sqrt (p) + 1; static map <T, T> mp; mp.clear (); fq (i, 1, t) { mul = mul * a % p; mp[b * mul % p] = i; } T mull = mul; fq (i, 1, t) { if (mp[mull]) return i * t - mp[mull]; mull = mull * mul % p; } return -1; } }; const _Math <> iMath; auto Math = iMath; } namespace lay { template <typename T = int> class _ExMath { private: static void expr_skpop (stack <char> &sk1, stack <T> &sk2, T fun (T, T, char)) { T r = sk2.top (); sk2.pop (); T l = sk2.top (); sk2.pop (); char op = sk1.top (); sk1.pop (); sk2.push (fun (l, r, op)); } public: static T expr (string s, map <char, short> mp, T fun (T, T, char)) { static stack <char> sk1; static stack <T> sk2; while (!sk1.empty ()) sk1.pop (); while (!sk2.empty ()) sk2.pop (); s = '(' + s + ')'; int len = s.size (); fnq (i, 0, len) { if (isdigit (s[i])) { T num = 0; while (isdigit (s[i])) num = num * 10 + s[i] - '0', ++i; --i; sk2.push (num); } elif (s[i] == '(') { sk1.push ('('); } elif (s[i] == ')') { while (sk1.top () != '(') expr_skpop (sk1, sk2, fun); sk1.pop (); } else { while (!sk1.empty () && sk1.top () != '(' && mp[sk1.top ()] >= mp[s[i]]) expr_skpop (sk1, sk2, fun); sk1.push (s[i]); } } return sk2.top (); } }; const _ExMath <> iExMath; auto ExMath = iExMath; } namespace lay { constexpr int Eular (int x) { int sqrtx = sqrt (x); int ans = x; fq (i, 2, sqrtx) if (x % i == 0) { ans = ans / i * (i - 1); while (x % i == 0) x /= i; } if (x > 1) ans = ans / x * (x - 1); return ans; } template <int p> class Modint { private: const static int eular = Eular (p); int num; static int unsafe_constructor (int x) {return (x % p + p) % p;} public: int toInt () {return num;} Modint () {} Modint (int x) {num = unsafe_constructor (x);} friend Modint operator + (Modint a, Modint b) {int c = a.num + b.num; if (c >= p) c -= p; return Modint (c);} friend Modint operator - (Modint a, Modint b) {int c = a.num - b.num; if (c < 0) c += p; return Modint (c);} friend Modint operator * (Modint a, Modint b) {return Modint <p> :: unsafe_constructor (a.num * b.num);} friend Modint operator / (Modint a, Modint b) {return Modint <p> :: unsafe_constructor (a.num * Math.power (b.num, p - 2, p));} friend Modint operator ^ (Modint a, Modint b) {return Math.power (a.num, b.num % Modint <p> :: eular, p);} friend Modint& operator += (Modint &a, Modint b) {return a = a + b;} friend Modint& operator -= (Modint &a, Modint b) {return a = a - b;} friend Modint& operator *= (Modint &a, Modint b) {return a = a * b;} friend Modint& operator /= (Modint &a, Modint b) {return a = a / b;} friend Modint& operator ^= (Modint &a, Modint b) {return a = a ^ b;} Modint& operator ++ () {*this += 1; return *this;} Modint operator ++ (signed) {Modint cpy = *this; ++(*this); return cpy;} Modint& operator -- () {*this -= 1; return *this;} Modint operator -- (signed) {Modint cpy = *this; --(*this); return cpy;} friend istream& operator >> (istream& in, Modint x) {x = rd (p); return in;} friend ostream& operator << (ostream&out, Modint x) {out << x.toInt (); return out;} }; } namespace lay { inline int lowbit (int i) {return i & -i;} template <int SZ> class BIT { int c[SZ]; int n; public: BIT () {} void set (int x) {n = x;} void clear () {fq (i, 0, n) c[i] = 0;} BIT (int x) {set (x);} void add (int i, int x) {while (i <= n) c[i] += x, i += lowbit (i);} int ask (int i) {int s = 0; while (i) s += c[i], i -= lowbit (i); return s;} int query (int l, int r) {return ask (r) - ask (l - 1);} }; } namespace lay { template <int p> struct _Cipolla { int i; struct comp { int a, b; }; comp mul (comp a, comp b) { return {(a.a * b.a + a.b * b.b % p * i) % p, (a.b * b.a + a.a * b.b) % p}; } int power (comp a, int b) { comp c = {1, 0}; while (b) { if (b & 1) c = mul (c, a); a = mul (a, a); b >>= 1; } return c.a; } pair <int, int> solve (int n) { if (n == 0) return {0, -1}; if (Math.power (n, (p - 1) >> 1, p) == p - 1) return {-1, -1}; mt19937 rnd(random_device {} ()); int c = -1; while (1) { c = rnd () % (p - 1) + 1; i = (c * c - n + p) % p; if (power ({i, 0}, (p - 1) >> 1) == p - 1) break; } int s = power ({c, 1}, (p + 1) >> 1); return {s, p - s}; } pair <int, int> operator () (int n) { return solve (n); } }; namespace poly { const int p = 998244353; const int g = 3, gi = Math.power (g, p - 2, p); const int I = Math.power (g, (p - 1) >> 2, p); struct _NTT { private: vi Turn; vi gs, gis; public: int size () {return Turn.size ();} void init (int len) { int n = 1; while (n < len) n <<= 1; Turn.resize (n); Turn[0] = 0; fnq (i, 1, n) Turn[i] = (Turn[i >> 1] | (i & 1 ? n : 1)) >> 1; } void ntt (vi &x, bool op) { int n = size (); if ((int)x.size () < n) x.resize (n); fnq (i, 0, n) if (i < Turn[i]) swap (x[i], x[Turn[i]]); int cc = 1; for (int h = 2; h <= n; h <<= 1, ++cc) { int Wn = (op ? gis[cc] : gs[cc]); for (int i = 0; i < n; i += h) { int w = 1; fnq (j, 0, h >> 1) { int a = x[i + j], b = 1ll * x[i + j + (h >> 1)] * w % p; x[i + j] = (a + b) % p; x[i + j + (h >> 1)] = (a - b + p) % p; w = 1ll * w * Wn % p; } } } if (op) { int inv = Math.power (n, p - 2, p); fnq (i, 0, n) x[i] = 1ll * x[i] * inv % p; } } _NTT () { gs.resize (25); fq (i, 1, 23) gs[i] = Math.power (g, (p-1) >> i, p); gis.resize (25); fq (i, 1, 23) gis[i]= Math.power(gi, (p-1) >> i, p); } } NTT; vi poly_mul (vi a, vi b) { NTT.init (a.size () + b.size () - 1); NTT.ntt (a, 0); NTT.ntt (b, 0); fnq (i, 0, NTT.size ()) a[i] = 1ll * a[i] * b[i] % p; NTT.ntt (a, 1); return a; } vi poly_inv (vi a, int n) { if ((int)a.size () > n) a.resize (n); if (n == 1) return {Math.power (a[0], p - 2, p)}; auto b = poly_inv (a, (n + 1) >> 1); auto c = poly_mul (a, b); fnq (i, 0, (int)c.size ()) c[i] = (p - c[i]) % p; c[0] = (c[0] + 2) % p; c = poly_mul (c, b); c.resize (n); return c; } vi poly_int (vi a, int c = 0) { vi b; b.pb (c); fnq (i, 0, (int)a.size ()) b.pb (1ll * Math.power (i+1, p-2, p) * a[i] % p); return b; } vi poly_diff (vi a) { vi b; fnq (i, 1, (int)a.size ()) b.pb (1ll * i * a[i] % p); return b; } vi poly_ln (vi a, int n) { if ((int)a.size () > n) a.resize (n); auto b = poly_diff (a); auto c = poly_mul (b, poly_inv (a, n)); c.resize (n + 1); auto e = poly_int (c, 0); return e; } vi poly_add (vi a, vi b) { vi c; c.resize (max (a.size (), b.size ())); fnq (i, 0, (int)c.size ()) c[i] = 0; fnq (i, 0, (int)a.size ()) c[i] = a[i]; fnq (i, 0, (int)b.size ()) c[i] = (c[i] + b[i]) % p; return c; } vi poly_sub (vi a, vi b) { vi c; c.resize (max (a.size (), b.size ())); fnq (i, 0, (int)c.size ()) c[i] = 0; fnq (i, 0, (int)a.size ()) c[i] = a[i]; fnq (i, 0, (int)b.size ()) c[i] = (c[i] + p - b[i]) % p; return c; } vi poly_exp (vi a, int n) { if ((int)a.size () > n) a.resize (n); if (n == 1) return {1}; auto b = poly_exp (a, (n + 1) >> 1); auto e = poly_ln (b, n); auto f = poly_sub (a, e); f[0] = (f[0] + 1) % p; auto g = poly_mul (b, f); g.resize (n); return g; } vi poly_mul (vi a, int b) { vi c; for (auto x : a) c.pb (1ll * x * b % p); return c; } vi poly_sin (vi a, int n) { auto b = poly_mul (a, I); auto be = poly_exp (b, n); auto c = poly_mul (a, (p-I) % p); auto ce = poly_exp (c, n); auto e = poly_sub (be, ce); auto f = poly_mul (e, Math.power (2 * I % p, p-2, p)); if ((int)f.size () > n) f.resize (n); return f; } vi poly_cos (vi a, int n) { auto b = poly_mul (a, I); auto be = poly_exp (b, n); auto c = poly_mul (a, (p-I) % p); auto ce = poly_exp (c, n); auto e = poly_add (be, ce); auto f = poly_mul (e, Math.power (2, p-2, p)); if ((int)f.size () > n) f.resize (n); return f; } vi poly_R (vi a, int n) { a.resize (n+1); fnq (i, 0, (n+1) >> 1) swap (a[i], a[n-i]); return a; } void poly_print(vi a, int n) { int lim = min ((int)a.size()-1, n); fq (i, 0, lim) cout << a[i] << " "; fq (i, lim+1, n) cout << "0 "; puts (""); } void poly_print(vi a) { int n = a.size () - 1; while (n && a[n] == 0) --n; poly_print (a, n); } void poly_seize (vi &a) { int n = a.size () - 1; while (n && a[n] == 0) --n; a.resize (n+1); } pair <vi, vi> poly_div (vi a, vi b) { poly_seize (a); poly_seize (b); int n = a.size()-1, m = b.size()-1; if (n < m) return {{0}, a}; auto ar = poly_R (a, n); auto br = poly_R (b, m); auto qr = poly_mul (ar, poly_inv (br, n-m+1)); auto q = poly_R (qr, n-m); auto r = poly_sub (a, poly_mul (b, q)); q.resize (n-m+1); r.resize (m); return {q, r}; } void poly_multi_eval_solve1 (vi &ps, int l, int r, int rt, vector <vi > &h) { if (l == r) { h[rt] = {(p - ps[l]) % p, 1}; return; } int mid = (l + r) >> 1; poly_multi_eval_solve1 (ps, l, mid, rt << 1, h); poly_multi_eval_solve1 (ps, mid + 1, r, rt << 1 | 1, h); h[rt] = poly_mul (h[rt << 1], h[rt << 1 | 1]); poly_seize (h[rt]); } void poly_multi_eval_solve2 (vi f, vi &ps, int l, int r, int rt, vector <vi > &h, vi &g) { f = poly_div (f, h[rt]).se; // if (l == r) { // g[l] = f[0]; // return; // } if (r - l + 1 <= 1024) { int n = f.size () - 1; fq (i, l, r) { int s = 0, x = ps[i]; nfq (j, n, 0) s = (1ll * s * x + f[j]) % p; g[i] = s; } return; } int mid = (l + r) >> 1; poly_multi_eval_solve2 (f, ps, l, mid, rt << 1, h, g); poly_multi_eval_solve2 (f, ps, mid + 1, r, rt << 1 | 1, h, g); } vi poly_multi_eval (vi &f, vi &ps) { int n = ps.size (); vector <vi> h; h.resize (n << 2); poly_multi_eval_solve1 (ps, 0, n-1, 1, h); vi g; g.resize (n); poly_multi_eval_solve2 (f, ps, 0, n-1, 1, h, g); return g; } void poly_fast_inter_solve1 (vector <pii> &ps, int l, int r, int rt, vector <vi > &h) { if (l == r) { h[rt] = {(p - ps[l].fi) % p, 1}; return; } int mid = (l + r) >> 1; poly_fast_inter_solve1 (ps, l, mid, rt << 1, h); poly_fast_inter_solve1 (ps, mid + 1, r, rt << 1 | 1, h); h[rt] = poly_mul (h[rt << 1], h[rt << 1 | 1]); poly_seize (h[rt]); } vi poly_fast_inter_solve2 (vector <pii> &ps, vi &gx, int l, int r, int rt, vector <vi > &h) { if (l == r) return {1ll * ps[l].se * Math.power (gx[l], p-2, p) % p}; int mid = (l + r) >> 1; auto vl = poly_mul (h[rt << 1 | 1], poly_fast_inter_solve2 (ps, gx, l, mid, rt << 1, h)); auto vr = poly_mul (h[rt << 1], poly_fast_inter_solve2 (ps, gx, mid + 1, r, rt << 1 | 1, h)); auto v = poly_add (vl, vr); poly_seize (v); return v; } vi poly_fast_inter (vector <pii> &ps) { int n = ps.size (); vector <vi> h; h.resize (n << 2); poly_fast_inter_solve1 (ps, 0, n-1, 1, h); auto gg = poly_diff (h[1]); vi xs; xs.resize (n); fnq (i, 0, n) xs[i] = ps[i].fi; auto gx = poly_multi_eval (gg, xs); auto f = poly_fast_inter_solve2 (ps, gx, 0, n-1, 1, h); return f; } // for a_0=1 vi poly_power (vi a, int k, int n) { auto b = poly_ln (a, n); auto c = poly_mul (b, {k}); auto e = poly_exp (c, n); return e; } // for all but a_0==0 and k is large and k%p is small vi poly_power (vi a, int n, int k1, int k2) { int len = min ((int)a.size (), n); int t = 0; while (t < len && a[t] == 0) ++t; if (t == len || t * k1 >= n) return vi (n, 0); int x = a[t], invx = Math.power (x, p-2, p); vi aa; fq (i, t, len - 1) aa.pb (a[i] * invx % p); int nn = n - t; auto b = poly_ln (aa, nn); auto c = poly_mul (b, {k1}); auto e = poly_exp (c, nn); auto ee = poly_mul (e, {Math.power (x, k2, p)}); vi f (t * k1, 0); int lim = n - t * k1; fnq (i, 0, lim) f.pb (ee[i]); return f; } pair <vi, vi> poly_sqrt (vi a, int n) { static _Cipolla <p> Cipolla; auto sqs = Cipolla (a[0]); int sq1 = sqs.fi, sq2 = sqs.se; if (sq1 == -1) return {{}, {}}; const int inv2 = Math.power (2, p-2, p); auto b = poly_mul (a, {Math.power (a[0], p-2, p)}); auto c = poly_ln (b, n); auto e = poly_mul (c, {inv2}); auto f = poly_exp (e, n); auto g1 = poly_mul (f, {sq1}); vi g2 = {}; if (sq2 != -1) g2 = poly_mul (f, {sq2}); return {g1, g2}; } } } // #define COUNTING_PROBLEM #if defined (COUNTING_PROBLEM) #define padd(x,y) (x = (x + y) % p) #endif void gmax (int &a, int b) {a = max (a, b);} void gmin (int &a, int b) {a = min (a, b);} using namespace lay; signed main () { return 0; } ```