假定每次整除平均需要 qqq 次,复杂度 O(qn),qn≥109O(qn),qn\ge10^9O(qn),qn≥109,显然不行。
出题人友好的用第三组样例提示了数组的循环性(我咋知道,他又没给完),加之题目背景和 3n+13n+13n+1 问题有关,所以也能和循环扯上关系,不妨从这个角度考虑。
既然题目背景有暗示,多测几组数据也能看出来(反正我是没看出来),这个数会在不断地迭代过程中变为 1,然后在 1 到 y−1y-1y−1 中循环。
下面推一下官解的公式:
- 第一步优化归一后的结果求解,这个循环的周期是 y−1y-1y−1,现在还剩 kkk 次计算,整个变化的数组长这样
{x,⋯ ,1,⋯ ,y−1,1⋯ ,y−1} \{{x,\cdots,1,\cdots ,y-1,1\cdots ,y-1\}} {x,⋯,1,⋯,y−1,1⋯,y−1}
众所周知(我还真不是很知),k mod y−1k\bmod y-1kmody−1 的取值是 {0,⋯ ,y−2}\{{0,\cdots,y-2\}}{0,⋯,y−2} ,所对应的值应当是 {1,⋯ ,y−2}\{{1,\cdots,y-2\}}{1,⋯,y−2},所以将剩余 kkk 次运算转化为结果就是(前提是 kkk 次运算后能够将给的 xxx 归一)
ans=1+k mod (y−1) ans=1+k\bmod(y-1) ans=1+kmod(y−1)
不能归一的话怎么办呢,最简单的就是直接把这个时候的 xxx 输出,但是官解压行了,因为这时候 k=0k=0k=0,显然有
ans=x+0 mod (y−1)=x ans=x+0\bmod(y-1)=x ans=x+0mod(y−1)=x
合并两类,推出最终公式:
ans=x+k mod (y−1) ans=x+k\bmod(y-1) ans=x+kmod(y−1) - 只做第一步优化连样例都过不了,因为数据给大之后凑整除需要的累加次数也会很大,很容易就超了,所以还要优化累加操作。
比如要让 161616 被 555 整除,很容易想到要凑 202020,也就是说找到 555 在 161616 后的一个倍数,首先确定 555 在 161616 大约是 165\frac{16}{5}516 倍,显然下一个倍数就是 ⌈165⌉×5=20\lceil\frac{16}{5}\rceil\times5=20⌈516⌉×5=20,也就是说,每次直接加上
op=⌈xy⌉×y−x op=\lceil\frac{x}{y}\rceil\times y-x op=⌈yx⌉×y−x
就行了。
当然如果这里使用ceil做取整运算还是超,因为这玩意其实是double __cdecl ceil(double _X);,并非是对整型的运算,算多了效率挺低,因此数学代换一下就成了(当然这个除是整除):
op=(xy+1)×y−x op=(\frac{x}{y}+1)\times y-x op=(yx+1)×y−x
第一个公式单从数学意义上来说,对于 y∈Z,op≥x≥0y\in Z,op\ge x\ge0y∈Z,op≥x≥0 好像没问题,但不要忘了这是整除,会出现 xy=0\frac xy=0yx=0 的情况,因此要确保每次至少加 111,同时还要确保每次累加操作不超过当前的剩余操作次数 kkk。
由于每次加都至少可以整除一次,所以每次操作至少将当前数减少
⌈x+1y⌉,y≥2 \lceil\frac{x+1}{y}\rceil,y\ge2 ⌈yx+1⌉,y≥2
显然时间复杂度优于二分,为 O(logn)O(\log n)O(logn)。
#include <bits/stdc++.h>
using namespace std;
template <typename A, typename B>
ostream &operator<<(ostream &os, const pair<A, B> &p)
{
return os << '(' << p.first << ", " << p.second << ')';
}
template<typename C,typename D = typename enable_if <!is_same<C, string>::value,typename C::value_type>::type>
ostream &operator<<(ostream &os, const C &v)
{
os << '{';
string sep;
for (const D &x : v)
os << sep << x, sep = ", ";
return os << '}';
}
void dbg_out()
{
cerr << endl;
}
template <typename Head, typename... Tail>
void dbg_out(Head H, Tail... T)
{
cerr << ' ' << H;
dbg_out(T...);
}
#ifndef ONLINE_JUDGE
#define dbg(...) cerr << "(" << #__VA_ARGS__ << "):", dbg_out(__VA_ARGS__)
#else
#define dbg(...)
#endif
int ____________________CYCLE____________________;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define inf INT32_MAX
#define i64 int64_t
const int N = 1e5;
void solve(int cycle);
int main(){
#ifndef ONLINE_JUDGE
freopen(".\\in.txt","r",stdin);
freopen(".\\out.txt","w",stdout);
#endif
ios::sync_with_stdio(false);
std::cin.tie(0);
int _=1;
cin >> _;
//cout << "Case #" << _ << ": " << endl;
for(____________________CYCLE____________________=1;____________________CYCLE____________________<=_;____________________CYCLE____________________++){
//cout << ____________________CYCLE____________________ << ":\n";
solve(____________________CYCLE____________________);
}
}
void solve(int cycle){
int x,y,k;
cin >> x >> y >> k;
int cnt = 0;
while(x != 1 && k > 0){
//dbg(ceil(x / y) - x);
int op = (x / y + 1)*y - x;
op = max(1,op);
op = min(op,k);
//dbg(x);
x += op;
while(x % y == 0){
x /= y;
//dbg(x);
}
k -= op;
}
dbg(cycle,x,y,k);
if(x == 1){
int num = 1 + (k % (y-1));
cout << num << endl;
}else{
cout << x << endl;
}
}

被折叠的 条评论
为什么被折叠?



