题解 CF1864C Divisor Chain

题意

给定一个整数 $x\ (2 \le x \le 10^9)$,你每次可以选择一个 $d \mid x$,并将 $x=x-d$。

你需要做这样的操作至多 $1000$ 次,并且不能使用同一个 $d$ 超过 $2$ 次。

求解将 $x$ 变为 $1$ 的操作序列。

分析

赛时找了很久规律,才发现是二进制拆分。

对 $x$ 进行二进制拆分,设 $x=\sum\limits_{i=1}^m2^{b_i}$, 满足 $\forall i \in [1,m) ,b_i>b_{i+1} \ge 0$。

观察可以发现:$$ \begin{matrix} \because a_1=x=\sum\limits_{i=1}^m2^{b_i}=2^{b_m}\sum\limits_{i=1}^m2^{b_i-b_m}\\ \therefore 2^{b_m} \mid a_1\\ \because a_2=a_1-2^{b_m}=\sum\limits_{i=1}^{m-1}2^{b_i}=2^{b_{m-1}}\sum\limits_{i=1}^{m-1}2^{b_i-b_{m-1}}\\ \therefore 2^{b_{m-1}} \mid a_2\\ \cdots\\ \therefore 2^{b_1} \mid a_m \end{matrix} $$ 所以可以证明:$$ \forall i\in[1,m],2^{b_{m-i+1}}\mid a_i $$ 所以我们可以从小到大依次减去 $2$ 的最低次幂,直到仅剩 $2$ 的最高次幂。

接着将 $2$ 的最高次幂依次砍半即可,易证每个减数不会使用超过 $2$ 次。

代码

//the code is from chenjh
#include<cstdio>
#include<algorithm>
#include<vector>
void solve(){
    int x;scanf("%d",&x);
    std::vector<int> v;//操作序列。
    v.emplace_back(x);
    int y=1<<std::__lg(x);//求 x 的 2 的最高次幂。
    int z=x-y;
    for(int i=0;i<=30;i++)if(z&(1<<i))v.emplace_back(x-=(1<<i));//从小到大减去这一位。
    while(y>1)v.emplace_back(y>>=1);//每次砍半。
    printf("%zu\n",v.size());
    for(const int y:v) printf("%d ",y);
    putchar('\n');
}
int main(){
    int T;scanf("%d",&T);
    while(T--) solve(); 
    return 0;
}
posted @ 2023-08-27 20:57  Chen_Jinhui  阅读(55)  评论(0)    收藏  举报  来源