前言
被大佬WeLikeStudying 花了几乎一下午的时间讲明白之后有感而发决定写一篇题解来报答dalao的恩情。
原题链接 :
U238921 大凯的疑惑(买得到的数目)
所以参考资料就是dalao的题解
题意
给定 $a,b$ ,求能被 $ax+by$ ( $x,y$ 是非负整数)表示出来的第 $k$ 小数的大小。
多测, $1 \le T \le 10^4$
题解
很相似的一道题是 小凯的疑惑(买不到的数目)
不过这题显然是那题的超级加强版(
暴力做法
首先考虑暴力,然后尝试优化。
可惜暴力也需要前置(
由裴蜀定理的推论可知最大的不能被表示的数为(官方题解也给出了这个定理,并且它有自己的名字叫“麦乐鸡定理”)(所以知道这个定理的话就可以爆切小凯了)
并且由同一个推论稍微走远一点就可以得出一个对称性 : $x$ 和 $ab-a-b-x$ 有且只有一个可以被表示出来, $ab-a-b-x+1$ 及以后的数都可以被表示,负数都不能被表示,我在 数论算法、定理和常用变换 的裴蜀定理板块给出了详细证明。
于是对于 $c > \frac{ab-a-b+1}{2} $ 的情况下我们可以 $O(1)$ 计算出答案。具体过程应当不必给出了。
剩下的情况只有 $c \le ab-a-b < ab$ ,所以显然有解时只有一组解。
考虑二分答案,然后枚举一个 $x$ 或 $y$ 进行验证。
我们可以轻松地得到这个式子 :
现在我们得到了显然不可行的 $O(min(a,b)\log k)$ 的做法力。
45pts做法
这才是一个常规题目的考察内容(
这个做法的内容更类似于小凯的疑惑那道题的证明。
我们可以去掉二分,把每个 $a$ 单独考虑,分别对其加上若干个 $b$ ,也就是 $a$ 个等差数列同时放到数轴上。
于是经过较为复杂的实现,我们得到了以下这份代码 :
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include<bits/stdc++.h> #include<algorithm> #include<cstdio> namespace Pozhu{ using namespace std; #define N 50000005 typedef long long ll; ll a,b,now,pos; bool have[N];
ll k,sum;
void main() { memset(have,0,sizeof(have)); scanf("%lld%lld%lld",&a,&b,&k); if(k > (a-1) * (b-1) >> 1) { printf("%lld\n",((a-1) * (b-1) >> 1) + k-1); return; } for(int i = 0;i <= a;i++) { if(!i) {have[0] = 1;continue;} ll count = (i * b / a - (i-1) * b / a) * i; if(k <= count || i == a) { ll block_nth = (i-1) * b / a + (k-1) / i; k = (k-1) % i; for(int j = 0;j < a;j++) { if(!have[j]) continue; k--; if(k == -1) { printf("%lld\n",block_nth * a + j); return; } } } else k -= count; have[b * i % a] = 1; } }
}
signed main() { int T; for(scanf("%d",&T);T;T--) Pozhu :: main(); return 0; }
|
100 pts做法
毒瘤一点,但用这个就可以去常规题目爆标(
考虑真的二分答案,然后用类欧优化一下check的过程
然后发现几乎成了一道原题P5171
(所以顺便切掉一黑)
我推式子的过程大概是这样的 :
虽然std使用了直接带着作为负数的 $-a$ 进行的一个推式子做到了极限数据依然不炸long long ,啊但是我不会推那个所以这种方法写__int128_t
也能过。
然后右边可以使用小学奥数 $O(1)$ 求得,左边用类欧求解。
最终我们就得到了复杂度为 $O(\log^2 k)$ 的正解,可以通过本题。
接下来的实现倒是简单的多……或许这才是真正数学题的样子
放下代码罢 :
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 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include<bits/stdc++.h> #include<algorithm> #include<cstdio> namespace Pozhu{ using namespace std; typedef __int128_t ill; ill a,b,k;
bool check(ill ); ill like_gcd(ill ,ill ,ill ,ill );
void main() { cin >> a >> b >> k; if(k > ((a-1) * (b-1) / 2)) { cout << (a-1) * (b-1) / 2 + k - 1 << '\n'; return; } if(a > b) swap(a,b); ill l = -1,r = a * b; while(l < r) { ill mid = (l + r + 1) / 2; if(check(mid)) l = mid; else r = mid - 1; } cout << l + 1 << '\n'; }
bool check(ill x) { return - (x / a) * (x / a + 1) / 2 + (x / a) + 1 + like_gcd(b-a,x,b,x/a) <= k - 1; }
ill like_gcd(ill a,ill b,ill c,ill n) { if(a == 0){return b/c * (n + 1);} if(a >= c || b >= c) { return n * (n + 1) / 2 * (a/c) + (n + 1) * (b/c) + like_gcd(a%c,b%c,c,n); } ill m = (a * n + b) / c - 1; if(m == -1) return 0; return n * (m + 1) - like_gcd(c,c-b-1,a,m); }
}
signed main() { int T; for(scanf("%d",&T);T;T--) Pozhu :: main(); return 0; }
|
(int128当然不能直接cin所以我本来有快读但是为了篇幅删掉力)
by Pozhu
2022.8.25