CodeForces 1622F Quadratic Set(结论+异或哈希+散列表)

problem

洛谷链接

solution

最后子集大小一定 ≥ n − 3 \ge n-3 n3,下面考虑证明这个结论。

  • 假设 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(2i1)!(2i)!=i=1k(((2i1)!)22i)=i=1k((2i1)!)2i=1k2i=i=1k((2i1)!)22kk!

    • 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(2i1)!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 n3(mod4),答案一定是扔掉 2 ! , n − 1 2 ! , n ! 2!,\frac{n-1}{2}!,n! 2!,2n1!,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(i1)!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 valk,答案为 n − 1 n-1 n1
    • 枚举删除的要删除的数 i i i,考虑 map 是否存在 v a l ⊕ g i ! val\oplus g_{i!} valgi!,如果有则将这个映射的 k k k 一同删去,答案为 n − 2 n-2 n2
  • 如果以上情况都没有发生,那么应当是 n ≡ 3 ( m o d 4 ) n\equiv 3\pmod4 n3(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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值