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;
}
posted @ 2023-02-06 19:14  ducati❤OI  阅读(49)  评论(0)    收藏  举报