problem
solution
最后子集大小一定 ≥ n − 3 \ge n-3 ≥n−3,下面考虑证明这个结论。
-
假设 n = 2 k n=2k n=2k。
∏ i = 1 n ( i ! ) = ∏ i = 1 k ( 2 i − 1 ) ! ( 2 i ) ! = ∏ i = 1 k ( ( ( 2 i − 1 ) ! ) 2 2 i ) = ∏ i = 1 k ( ( 2 i − 1 ) ! ) 2 ⋅ ∏ i = 1 k 2 i = ∏ i = 1 k ( ( 2 i − 1 ) ! ) 2 ⋅ 2 k ⋅ k ! \prod_{i=1}^n(i!)=\prod_{i=1}^{k}(2i-1)!(2i)!=\prod_{i=1}^{k}\Big(\big((2i-1)!\big)^22i\Big)=\prod_{i=1}^k\big((2i-1)!\big)^2·\prod_{i=1}^{k}2i=\prod_{i=1}^k\big((2i-1)!\big)^2·2^k·k! ∏i=1n(i!)=∏i=1k(2i−1)!(2i)!=∏i=1k(((2i−1)!)22i)=∏i=1k((2i−1)!)2⋅∏i=1k2i=∏i=1k((2i−1)!)2⋅2k⋅k!
- k = 2 t k=2t k=2t,只用扔掉 k ! k! k! 这一项,就可以开出整根 ∏ i = 1 k ( 2 i − 1 ) ! ⋅ 2 t \prod_{i=1}^k(2i-1)!·2^t ∏i=1k(2i−1)!⋅2t。
- k = 2 t + 1 k=2t+1 k=2t+1,扔掉 k ! k! k! ,再多扔一个 2 ! 2! 2!,仍可以开出上面的整根。
-
假设 n = 2 k + 1 n=2k+1 n=2k+1。
直接扔掉 n ! n! n!,就转化到了 n ′ = 2 k n'=2k n′=2k 的情况。
且如果情况满足 n ≡ 3 ( m o d 4 ) n\equiv3\pmod4 n≡3(mod4),答案一定是扔掉 2 ! , n − 1 2 ! , n ! 2!,\frac{n-1}{2}!,n! 2!,2n−1!,n!。
这里只是证明的答案的下界,但并非一定是最优答案的扔法(通过样例可知)。
接着我们需要考虑如何判断是否可以只用删除 1 / 2 1/2 1/2 个数。
要知道,决定一个数是否是平方数的关键在于质因子的幂次是否都是偶数。
也就是说要在尽可能地少扔数条件下,使得质因子幂次为奇数的都变成偶数。
只有出现次数的奇偶之分,我们想到了 0 / 1 0/1 0/1。
两个数相乘,对应质因子的幂次相加,二进制的加法相当于 01 01 01 的异或。
但不可能对于每一个 i i i 都把 n n n 以内的质数都开一个空间,然后储存奇偶之分,再异或。时间空间总得挂一个。
1 e 6 1e6 1e6 以内的质数个数 $\approx\frac{n}{\ln n}\approx 78498 $。我们采用 异或哈希。
对每一个质数分配随机的 64 64 64 位整型权值,两个数的乘积变为两个数的哈希值的异或。
这样一来,对于出现偶数次的质数,异或后结果就是 0 0 0,只有出现奇数次的质数的权值会呈现在结果中。
质数 i i i 的哈希值记为 g i g_i gi,同样还要知道 g i ! g_{i!} gi!,这是很好求的 g i ! = g ( i − 1 ) ! ⊕ g i g_{i!}=g_{(i-1)!}\oplus g_i gi!=g(i−1)!⊕gi。
因为 1 1 1 肯定是必选的,请直接从头到尾忽视。
- 如果 v a l = ⨁ i = 2 n g i ! = 0 val=\bigoplus_{i=2}^n g_{i!}=0 val=⨁i=2ngi!=0,则答案为 n n n。
- 将
g
i
!
g_{i!}
gi! 通过
map
映射 i i i。- 如果
map
中存在一个值为 v a l val val,意味着可以删去这个 v a l → k val\rightarrow k val→k,答案为 n − 1 n-1 n−1。 - 枚举删除的要删除的数
i
i
i,考虑
map
是否存在 v a l ⊕ g i ! val\oplus g_{i!} val⊕gi!,如果有则将这个映射的 k k k 一同删去,答案为 n − 2 n-2 n−2。
- 如果
- 如果以上情况都没有发生,那么应当是 n ≡ 3 ( m o d 4 ) n\equiv 3\pmod4 n≡3(mod4) 的情况,直接删掉特定的三个数即可。
我也不知道这个哈希的冲撞概率是多少,太菜了 so vegetable!
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define int long long
map < int, int > mp;
int n, cnt;
int f[maxn], g[maxn], prime[maxn];
signed main() {
mt19937_64 Rand( time( 0 ) );
scanf( "%lld", &n );
iota( prime + 1, prime + n + 1, 1 );
for( int i = 2;i <= n;i ++ )
if( prime[i] == i ) {
for( int j = i;j <= n;j += i )
prime[j] = min( prime[j], i );
g[i] = Rand();
}
for( int i = 2;i <= n;i ++ ) {
f[i] = f[i - 1];
int x = i;
while( x ^ 1 ) {
f[i] ^= g[prime[x]];
x /= prime[x];
}
mp[f[i]] = i;
}
auto solve = [&] ( int n ) -> vector < int > {
int ans = 0;
for( int i = 2;i <= n;i ++ ) ans ^= f[i];
if( ! ans ) return {};
if( mp.find( ans ) != mp.end() ) return { mp[ans] };
for( int i = 2;i <= n;i ++ )
if( mp.find( ans ^ f[i] ) != mp.end() ) return { i, mp[ans ^ f[i]] };
return { 2, n - 1 >> 1, n };
};
auto ans = solve( n );
printf( "%lld\n", n - ans.size() );
for( int i = 1;i <= n;i ++ )
if( find( ans.begin(), ans.end(), i ) == ans.end() ) printf( "%lld ", i );
return 0;
}