Codeforce 1982B Collatz Conjecture

假定每次整除平均需要 qqq 次,复杂度 O(qn),qn≥109O(qn),qn\ge10^9O(qn),qn109,显然不行。
出题人友好的用第三组样例提示了数组的循环性(我咋知道,他又没给完),加之题目背景和 3n+13n+13n+1 问题有关,所以也能和循环扯上关系,不妨从这个角度考虑。
既然题目背景有暗示,多测几组数据也能看出来(反正我是没看出来),这个数会在不断地迭代过程中变为 1,然后在 1 到 y−1y-1y1 中循环。
下面推一下官解的公式:

  1. 第一步优化归一后的结果求解,这个循环的周期是 y−1y-1y1,现在还剩 kkk 次计算,整个变化的数组长这样
    {x,⋯ ,1,⋯ ,y−1,1⋯ ,y−1} \{{x,\cdots,1,\cdots ,y-1,1\cdots ,y-1\}} {x,,1,,y1,1,y1}
    众所周知(我还真不是很知),k mod y−1k\bmod y-1kmody1 的取值是 {0,⋯ ,y−2}\{{0,\cdots,y-2\}}{0,,y2} ,所对应的值应当是 {1,⋯ ,y−2}\{{1,\cdots,y-2\}}{1,,y2},所以将剩余 kkk 次运算转化为结果就是(前提是 kkk 次运算后能够将给的 xxx 归一)
    ans=1+k mod (y−1) ans=1+k\bmod(y-1) ans=1+kmod(y1)
    不能归一的话怎么办呢,最简单的就是直接把这个时候的 xxx 输出,但是官解压行了,因为这时候 k=0k=0k=0,显然有
    ans=x+0 mod (y−1)=x ans=x+0\bmod(y-1)=x ans=x+0mod(y1)=x
    合并两类,推出最终公式:
    ans=x+k mod (y−1) ans=x+k\bmod(y-1) ans=x+kmod(y1)
  2. 只做第一步优化连样例都过不了,因为数据给大之后凑整除需要的累加次数也会很大,很容易就超了,所以还要优化累加操作。
    比如要让 161616555 整除,很容易想到要凑 202020,也就是说找到 555161616 后的一个倍数,首先确定 555161616 大约是 165\frac{16}{5}516 倍,显然下一个倍数就是 ⌈165⌉×5=20\lceil\frac{16}{5}\rceil\times5=20516×5=20,也就是说,每次直接加上
    op=⌈xy⌉×y−x op=\lceil\frac{x}{y}\rceil\times y-x op=yx×yx
    就行了。
    当然如果这里使用 ceil 做取整运算还是超,因为这玩意其实是 double __cdecl ceil(double _X);,并非是对整型的运算,算多了效率挺低,因此数学代换一下就成了(当然这个除是整除):
    op=(xy+1)×y−x op=(\frac{x}{y}+1)\times y-x op=(yx+1)×yx
    第一个公式单从数学意义上来说,对于 y∈Z,op≥x≥0y\in Z,op\ge x\ge0yZ,opx0 好像没问题,但不要忘了这是整除,会出现 xy=0\frac xy=0yx=0 的情况,因此要确保每次至少加 111,同时还要确保每次累加操作不超过当前的剩余操作次数 kkk
    由于每次加都至少可以整除一次,所以每次操作至少将当前数减少
    ⌈x+1y⌉,y≥2 \lceil\frac{x+1}{y}\rceil,y\ge2 yx+1,y2
    显然时间复杂度优于二分,为 O(log⁡n)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;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yyt363045841

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值