本文最后更新于 2026年2月21日 下午
-1 前言
神人题目。
记得当时是2024年3月3日下午,当时机房里面几个大佬去了省选~~,虽然当时我没有去省选~~,但是这题出在luogu的第一时间我就开始做这题。想了非常长时间,各种结论非常混乱,最后还没能做出来。接近两年之后的省选前夕竟然又看到了这题,两年过去水平应该是有了巨大的变化,但是相同的是依旧没有去省选的资格/ll。
既然说季风和题意还有做法有关系,那么接下来我将严肃思考我常常追忆过去和 bitset 的联系。
0 芝士
1 题意
为避免混淆,下文使用 X,Y 代替原文中给定数值 x,y,并且区别于题目的是,下文序列全部为1-index,这并不会影响做题。
题面非常形式化。
但是这个题真的和季风有关系的,把 (x,y) 看成目标点,(xi′,yi′) 看成自主移动,k 为限制,(xi,yi) 看成季风扰动,问题就是求要移动到目标点的最小步数。如果题面这么写应该会更好看也会更好理解些。
这么说我的追忆旅程疑似是一张DAG。
2 思路
下文参考了Luogu题解区第1,3篇题解,在此感谢。
首先还是,因为有题目里面有这种循环的 x 和 y,所以考虑先把这两个当作无限循环的序列。
先来转化一下题目里面的性质。容易发现我们不关心具体的 x′ 和 y′ 序列,所以对于每一条的具体限制 ∣xi′∣+∣yi′∣≤k 并不重要,只要总体上不超限制就一定可以调整出一组合法的 xi′ 和 yi′ 来。所以有以下式子:
∑m∣xi′∣+∑m∣yi′∣≤m⋅k=∣∑mxi−X∣+∣∑myi−Y∣≤m⋅k
这个对的原因是我们一定可以让 x′ 和 y′ 整个序列的正负相同,因为序列中同时出现正数和负数肯定可以相加抵消成一个数。
然后是经典的思路之差绝对值:
∑m(xi+yi−k)≤X+Y∑m(−xi+yi−k)≤−X+Y∑m(xi−yi−k)≤X−Y∑m(−xi−yi−k)≤−X−Y
上面的移一下项就是很好推的。为了下面代码实现方便再给上面四个全都两边取反:
∑m(xi+yi+k)≥X+Y∑m(−xi+yi+k)≥−X+Y∑m(xi−yi+k)≥X−Y∑m(−xi−yi+k)≥−X−Y
可以发现,上面这四个同时满足的 m 才是合法的 m。
这四个式子只有 x,y 和 X,Y 的符号有点小差别,因此我们先用第一个来推一推式子,其余的类似。
考虑由于 x,y 是无限循环的,所以令 m=hn+b。同时令 prej=∑i=1j(xi+yi+k)。则式子可以表示为:
h⋅pren+preb≥X+Y
显然这个时候 b 是可以枚举的,于是考虑枚举 b,求出对应 h 的区间:
-
若 pren=0,则当且仅当 preb≥X+Y 时解为全非负整数,否则解集为空。
-
若 pren>0,那么
h≥⌈prenX+Y−preb⌉
并且要注意 h 也是非负整数,即上式要满足 X+Y≥preb,否则解集为 h≥0。
-
若 pren<0,那么和上文类似的:
h≤⌊prenX+Y−preb⌋
同样的,上式要满足 X+Y≤preb,否则解集为空。
枚举完 b 我们就得到了 n 个解集。对其他三个式子做一遍类似的过程,得到对应 b 的解集取交,得到每个 b 的最终解集 [li,ri]。然后再次枚举一遍 b 算答案,也就是 ans←minb=1n{li⋅n+b},就做完了。
细节好像也没什么细节,就是要记得特判一下 X=Y=0 的情况,否则过不去样例。
3 代码
需要注意多测清空和向上取整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| int n,k,x,y,a[N],b[N],pre[N],l[N],r[N]; void solve(int fx,int fy){ for(int i=1;i<=n;++i){pre[i]=pre[i-1]+fx*a[i]+fy*b[i]+k;} int xx=fx*x,yy=fy*y; if(pre[n]==0){ for(int i=1;i<=n;++i){if(pre[i]<xx+yy)r[i]=ivl;} return; } for(int i=1;i<=n;++i){ if(pre[n]>0){ if(pre[i]<=xx+yy)l[i]=std::max(l[i],(xx+yy-pre[i])/pre[n]+((xx+yy-pre[i])%pre[n]?1:0)); else l[i]=; }else{ if(pre[i]<xx+yy)r[i]=ivl; else r[i]=std::min(r[i],(xx+yy-pre[i])/pre[n]); } } } signed main(){ int T=read(); while(T--){ r[0]=ivl; n=read(),k=read(),x=read(),y=read(); for(int i=1;i<=n;++i){ l[i]=0;r[i]=inf; } for(int i=1;i<=n;++i){a[i]=read(),b[i]=read();} if(!x&&!y){printf("0\n");continue;} solve(1,1); solve(1,-1); solve(-1,1); solve(-1,-1); int ans=inf; for(int i=1;i<=n;++i){ if(l[i]<=r[i]){ ans=std::min(ans,i+l[i]*n); } } if(ans==inf)printf("-1\n"); else printf("%lld\n",ans); } return 0; }
|
4 时间复杂度分析
没啥好分析的,简单 O(n)。
5 后记
神人题目。
生日愿望是能去 WC2027。我有生之年一定要在WC的舞台上唱白鸟过河滩!!!!1