一、题目
二、解法
首先手玩可以得到一个貌似没什么用的性质,也就是最后是否是平方数取决于 \(\prod_{k\geq 0}(n-2k)\) 是否是平方数,由于我们还想要更简单的形式,这里我们不妨先只考虑 \(n\) 为偶数的情况:
\[\prod_{0<2k\leq n} 2k=2^{n/2}\cdot (n/2)!
\]
那么我们在最坏情况下也只需要删除 \(2,n/2,n\) 这三项就可以获得构造,这说明答案的下界为 \(3\),所以我们只需要讨论答案是否为 \(0/1/2\),剩下的情况就给出 \(3\) 的上述构造即可。
\(0\) 可以直接判断乘积是不是完全平方数,直接对 \(\prod_{k\geq 0} (n-2k)\) 质因数分解即可;\(1\) 就需要找出质因数分解和上式相同的一个数,可以用哈希判断,就是给每个质因子分配一个 \(\tt ull\) 之内的整数,然后判断异或和是否相等即可;\(2\) 可以加上 \(\tt unordered\_map\) 来找异或和相等的点对。
三、总结
注意构造题有这样一种情况,也就是当答案范围很小的时候我们可以用讨论法解决问题,排除掉所有简单情况之后剩下的就是那种较复杂的情况。
#include <cstdio>
#include <cstdlib>
#include <unordered_map>
#include <ctime>
using namespace std;
const int M = 1000005;
#define ull unsigned long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,cnt,mn[M],a[M],p[M],vis[M];ull v[M],h[M];
unordered_map<ull,int> mp;
int random(int x)
{
return (rand()*rand()+rand())%x;
}
void init()
{
for(int i=2;i<=n;i++)
{
if(!vis[i]) p[++cnt]=i,mn[i]=i;
for(int j=1;j<=cnt && i*p[j]<=n;j++)
{
vis[i*p[j]]=1;mn[i*p[j]]=p[j];
if(i%p[j]==0) break;
}
}
}
signed main()
{
n=read();init();srand(time(0));
for(int i=1;i<=n;i++)
v[i]=1ll*random(2e9)*random(2e9)+random(2e9);
for(int i=n;i>=1;i-=2)
{
int x=i;
while(x>1)
{
int p=mn[x];
while(x%p==0) x/=p,a[p]^=1;
}
}
int fl=1;for(int i=1;i<=n;i++) fl&=!a[i];
if(fl)
{
printf("%d\n",n);
for(int i=1;i<=n;i++)
printf("%d ",i);
puts("");return 0;
}
ull hs=0;
for(int i=1;i<=n;i++) if(a[i]) hs^=v[i];
for(int i=1;i<=n;i++)
{
int x=i;
while(x>1)
{
int p=mn[x];
while(x%p==0) x/=p,h[i]^=v[p];
}
h[i]^=h[i-1];
if(h[i]==hs)
{
printf("%d\n",n-1);
for(int j=1;j<=n;j++) if(j!=i)
printf("%d ",j);
puts("");return 0;
}
}
for(int i=1;i<=n;i++)
{
if(mp.count(hs^h[i]))
{
int j=mp[hs^h[i]];
printf("%d\n",n-2);
for(int k=1;k<=n;k++) if(k!=i && k!=j)
printf("%d ",k);
puts("");return 0;
}
mp[h[i]]=i;
}
printf("%d\n",n-3);
for(int i=1;i<n;i++) if(i!=2 && i!=n/2)
printf("%d ",i);
puts("");return 0;
}