[骗分技巧——随机化Ⅰ]CodeChef-Milestones,CF364D-Ghd

本文探讨了随机化算法的期望复杂度,包括CodeChef中的两个问题解决策略:一是通过概率分析求解线性覆盖问题的期望次数,二是利用随机贪心法确定子集最大公约数。第一个问题中,算法期望在七次尝试后覆盖所有点;第二个问题中,通过随机选择元素构建子集,最大化gcd。
摘要由CSDN通过智能技术生成


设随机化一次的正确率为 p p p,一次的复杂度为 O ( f ( n ) ) O(f(n)) O(f(n))。则随机的期望次数 k k k
k = ∑ i = 1 ∞ p ( 1 − p ) i − 1 i ( 1 ) ( 1 − p ) k = ∑ i = 1 ∞ p ( 1 − p ) i i = ∑ i = 2 ∞ p ( 1 − p ) i − 1 ( i − 1 ) ( 2 ) ( 1 ) − ( 2 ) ⇒ p ⋅ k = p + ∑ i = 2 ∞ p ( 1 − p ) i − 1 ⇒ k = 1 + ∑ i = 2 ∞ ( 1 − p ) i − 1 = ∑ i = 0 ∞ ( 1 − p ) i k=\sum_{i=1}^∞p(1-p)^{i-1}i\quad\quad\quad(1)\\\\ (1-p)k=\sum_{i=1}^∞p(1-p)^ii=\sum_{i=2}^∞p(1-p)^{i-1}(i-1)\quad (2)\\\\ (1)-(2)\Rightarrow p·k=p+\sum_{i=2}^∞p(1-p)^{i-1}\Rightarrow k=1+\sum_{i=2}^∞(1-p)^{i-1}=\sum_{i=0}^∞(1-p)^i k=i=1p(1p)i1i(1)(1p)k=i=1p(1p)ii=i=2p(1p)i1(i1)(2)(1)(2)pk=p+i=2p(1p)i1k=1+i=2(1p)i1=i=0(1p)i
由等比公式得: k = ∑ i = 0 ∞ ( 1 − p ) i = 1 − ( 1 − p ) ∞ 1 − ( 1 − p ) = 1 p k=\sum_{i=0}^∞(1-p)^i=\frac{1-(1-p)^∞}{1-(1-p)}=\frac{1}{p} k=i=0(1p)i=1(1p)1(1p)=p1

综上随机算法求解的期望复杂度为 O ( p − 1 f ( n ) ) O(p^{-1}f(n)) O(p1f(n))

CodeChef-Milestones

problem

题目链接

solution

因为最多只有七条直线就可以覆盖所有点,可以推出 覆盖点最多的直线至少覆盖 ⌈ n 7 ⌉ \lceil\frac n7\rceil 7n 个点

也就是说,两个点属于同一直线的概率为 1 7 \frac{1}{7} 71

期望来说, 7 7 7 次随机化点就可以通过了,但是保险起见,可以运行 1000 1000 1000 次。

时间复杂度 O ( T n k ) O(Tnk) O(Tnk),超了但是跑得很快》》o_o …

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 10005
int T, n;
double x[maxn], y[maxn];

int main() {
    scanf( "%d", &T );
    while( T -- ) {
        scanf( "%d", &n );
        for( int i = 1;i <= n;i ++ ) scanf( "%lf %lf", &x[i], &y[i] );
        mt19937 wwl(time(0));
        uniform_int_distribution < int > range( 1, n );
        int ans = 0;
        for( int t = 1;t <= 1000;t ++ ) {
            int a = range( wwl ), b = range( wwl );
            int cnt = 0;
            if( x[a] == x[b] ) {
                for( int i = 1;i <= n;i ++ )
                    if( x[i] == x[a] ) cnt ++;
            }
            else {
                cnt = 1;
                double k = ( y[a] - y[b] ) / ( x[a] - x[b] );
                for( int i = 1;i <= n;i ++ )
                    if( x[i] != x[a] and ( y[i] - y[a] ) / ( x[i] - x[a] ) == k ) 
                        cnt ++;
            }
            ans = max( ans, cnt );
        }
        printf( "%d\n", ans );
    }
    return 0;
}

CF364D-Ghd

problem

题目链接

solution

贪心地, ∣ S ′ ∣ |S'| S 选出的子集大小应该恰好为 ⌈ n 2 ⌉ \lceil\frac{n}{2}\rceil 2n,因为选择的数越多, gcd ⁡ \gcd gcd 只会受到更多限制变小而不会变大。

那么每个数都是 1 2 \frac 1 2 21 的概率会属于最后答案子集。

随机化 10 10 10 次,正确率 1 − ( 1 − 1 2 ) 10 1-(1-\frac 1 2)^{10} 1(121)10 已经很优秀了。

随机化出一个数 a x a_x ax 假设其属于最后答案子集。

先求出 a x a_x ax 的所有因子数,最后的答案肯定是 a x a_x ax 的因子。用桶 c n t p cnt_p cntp 记录下来。

再求出与 a i a_i ai gcd ⁡ \gcd gcd,枚举 gcd ⁡ \gcd gcd 的因子数,找到因子所在的桶, c n t p + + cnt_p++ cntp++

最后进行桶的合并,如果桶 p p p 代表的因子是桶 q q q 代表的因子倍数,则有 c n t q + = c n t p cnt_q+=cnt_p cntq+=cntp

1 e 12 1e12 1e12 内因子数最多的不超过 6800 6800 6800 个。

单次时间复杂度,假设 δ ( n ) \delta(n) δ(n) n n n 的因子个数, O ( a x + δ ( n ) 2 + n log ⁡ n ) O(\sqrt{a_x}+\delta(n)^2+n\log n) O(ax +δ(n)2+nlogn)

code

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 1000005
int n, ans, tot;
int a[maxn], d[maxn], cnt[maxn];

int gcd( int x, int y ) {
    if( ! y ) return x;
    else return gcd( y, x % y );
}

void divide( int x ) {
    tot = 0;
    for( int i = 1;i * i <= x;i ++ )
        if( x % i == 0 ) {
            d[++ tot] = i;
            if( i * i != x ) d[++ tot] = x / i;
        }
    sort( d + 1, d + tot + 1 );
}

void solve( int x ) {
    divide( x );
    for( int i = 1;i <= tot;i ++ ) cnt[i] = 0;
    for( int i = 1;i <= n;i ++ ) {
        int g = gcd( x, a[i] );
        int pos = lower_bound( d + 1, d + tot + 1, g ) - d;
        cnt[pos] ++;
    }
    for( int i = 1;i <= tot;i ++ )
        for( int j = i + 1;j <= tot;j ++ )
            if( d[j] % d[i] == 0 ) cnt[i] += cnt[j];
    for( int i = tot;i;i -- ) {
        if( d[i] <= ans ) break;
        if( cnt[i] >= ( n + 1 >> 1 ) ) ans = max( ans, d[i] );
    }
}

signed main() {
    scanf( "%lld", &n );
    for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );
    mt19937 wwl(time(0));
    uniform_int_distribution < int > range( 1, n );
    for( int t = 1;t <= 10;t ++ ) {
        int x = range( wwl );
        solve( a[x] );
    }
    printf( "%lld\n", ans );
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值