CF1637G Birthday 题解
Description
Solution
手玩几组可以发现,最终相同的数 \(x\) 只能是 \(2\) 的次幂。证明是容易的,假设奇质数 \(p\) 不能整除 \(x\),那么对于任意数 \(y\),\(x+y,|x-y|\) 至少一者不能被 \(p\) 整除,那么我们无法消除所有无法被 \(p\) 整除的数。
注意到对 \(0,x\) 进行连续的两次操作会得到 \(0,2x\)。那么,我们只需要使序列中存在 \(0\),剩下的均为 \(2\) 的次幂,即可完成构造。
关键在于,如何将所有数均变为 \(2\) 的次幂,且其中的最大值最小。令 \(m\) 为 \(\le n\) 的最大 \(2\) 的次幂。我们考虑将 \((m-1,m+1),(m-2,m+2),\cdots\) 两两配对,这样会得到若干 \(2m\),若干 \(2,4,6,\cdots,2(n-m)\) 以及 \(1,2,3,\cdots,2m-n-1\)。那么,我们得到了大小为 \(n-m\) 和 \(2m-n-1\) 的子问题,递归即可。边界为 \(n \le 2\)。
注意到,递归下一层的总长为 \(m-1\),那么递归总层数是 \(O(\log n)\) 的。最后,我们还要将每个数倍增到 \(2^{\lceil \log_2 n \rceil}\),每个数的倍增次数也是 \(O(\log n)\) 的。总操作次数 \(O(n \log n)\),常数远远不及 \(2\),完全跑不满,可以通过。
Code
#include <bits/stdc++.h>
using namespace std;
const int N=65540;
int read(){
int s=0,w=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') w=-w;ch=getchar();}
while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
return s*w;
}
int n,m,buc[N]; vector<pair<int,int> >vec;
void op(int x,int y){
vec.emplace_back(make_pair(x,y));
buc[x]--,buc[y]--,buc[x+y]++,buc[abs(x-y)]++;
}
void get_m(){
m=1;
while (m<n) m<<=1;
}
void work(int n,int tag){
if (n<=2) return;
int p=1;
while (p<=n) p<<=1;
p>>=1;
for (int x=p-1,y=p+1;y<=n;x--,y++) op(x<<tag,y<<tag);
work(n-p,tag+1),work((p<<1)-n-1,tag);
}
void doub(){
int x;
for (int i=1;i<m;i<<=1){
if (buc[i]>1) {x=i;break;}
}
op(x,x);
for (int i=1;i<m;i<<=1){
while (buc[i]) op(0,i),op(i,i);
}
op(0,m);
}
void solve(){
n=read(),get_m(),vec.clear();
if (n<=2) return puts("-1"),void();
for (int i=1;i<=m;i++) buc[i]=(i<=n);
work(n,0),doub(),printf("%d\n",(int)vec.size());
for (auto t:vec) printf("%d %d\n",t.first,t.second);
}
int main(){
int T=read();
while (T--) solve();
return 0;
}

浙公网安备 33010602011771号