【PKUWC2018】随机算法

Description

我们知道,求任意图的最大独立集是一类NP完全问题,目前还没有准确的多项式算法,但是有许多多项式复杂度的近似算法。

例如,小 C 常用的一种算法是:

1.对于一个 \(n\) 个点的无向图,先等概率随机一个 \(1\ldots n\) 的排列 \(p[1\ldots n]\)。

2.维护答案集合 \(S\) ,一开始 \(S\) 为空集,之后按照 \(i=1\ldots n\)的顺序,检查 \(\{p[i]\}\cup S\) 是否是一个独立集,如果是的话就令 \(S=\{p[i]\}\cup S\)。

3.最后得到一个独立集 \(S\) 作为答案。

小 C 现在想知道,对于给定的一张图,这个算法的正确率,输出答案对 \(998244353\) 取模

Input

第一行两个非负整数 \(n,m\) 表示给定的图的点数和边数。

接下来 \(m\) 行,每行有两个正整数 \((u,v) (u\neq v)\)描述这张图的一条无向边。

Output

输出正确率,答案对 \(998244353\) 取模。

Solution

看到\(n \leq 20\) 时,有经验的选手应该就会往状压DP上去想.

令\(F_{i,j}\)表示当前独立集大小为i,不能选的点集为j的方案数,\(S_u\)表示选了\(i\)后不能选的点的集合,易得

\[ F_{i,j\bigcup S_u}=F_{i,j\bigcup S_u}+F_{i-1,j}*A_{n-\mid j \mid -1}^{\mid j\bigcup S_u \mid - \mid j \mid -1 } u \notin j\]

复杂度 \(O(2^n * n^2)\),这看起来已经很优秀了。

继续观察,发现每个状态的最大独立集是唯一的

记\(S_j\)表示不能选的点集为j时最大的独立集大小

每次更新\(S_j\)时将\(F_j\)清零即可

复杂度变为\(O(2^n*n)\)

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int Mod=998244353;
int n,m,cou[1500000],state[50],num[1500000];
long long J[50],dp[1500000],R[50];
long long fpow(long long a,int k)
{
    long long ans=1;
    while (k)
    {
        if (k&1) ans=ans*a%Mod;
        a=a*a%Mod;
        k>>=1;
    }
    return ans;
}
long long A(int n,int m)
{
    return (n>=m?J[n]*R[n-m]%Mod:0);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<(1<<n);i++) cou[i]=cou[i>>1]+(i&1);
    for (int i=1;i<=n;i++) state[i]|=1<< i-1;
    for (int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        state[u]|=1<< v-1;
        state[v]|=1<< u-1;
    }
    J[0]=1;
    for (int i=1;i<=n;i++) J[i]=J[i-1]*i%Mod;
    for (int i=0;i<=n;i++) R[i]=fpow(J[i],Mod-2);
    dp[0]=1;num[0]=0;
    for (int i=0;i<(1<<n);i++)
    {
        if (!dp[i]) continue;
        for (int j=1;j<=n;j++)
        {
            if ((i>>(j-1)) & 1) continue;
            if (num[i]+1>num[i|state[j]]) num[i|state[j]]=num[i]+1,dp[i|state[j]]=0;
            if (num[i]+1==num[i|state[j]]) dp[i|state[j]]=(dp[i|state[j]]+dp[i]*A(n-cou[i]-1,cou[i|state[j]]-cou[i]-1)%Mod)%Mod;
        }
    }
    printf("%lld\n",dp[(1<<n)-1]*fpow(J[n],Mod-2)%Mod);
    return 0;
}

原文地址:https://www.cnblogs.com/Code-Geass/p/10291705.html

时间: 2024-11-06 11:35:05

【PKUWC2018】随机算法的相关文章

PKUWC2018 随机算法

给你一个$n$个点$m$条边的无向图,执行如下算法: 1.随机一个$1~n$的排列$P$ 2.从$P$中按顺序一个一个将点加进独立集$S$里,始终保证$S$是独立集(即如果当前点和当前集合里的某个点相邻,就不加了) 求最后得到的$S$是原图的一个最大独立集的概率 $50% n \leq 17$ $100% n \leq 20$ sol: 先考虑部分分吧 很暴力的状压dp,$F[S1][S2]$表示选了$S1$中的点,当前最大独立集是$S2$的方案数,最后除以$n!$就可以了 转移是枚举每个点,所

LOJ2540 [PKUWC2018] 随机算法 【状压DP】

题目分析: 听说这题考场上能被$ O(4^n) $的暴力水过,难不成出题人是毕姥爷? 首先思考一个显而易见的$ O(n^2*2^n) $的暴力DP.一般的DP都是考虑最近的加入了哪个点,然后删除后递归进行状压DP.由于这道题的题目询问方式是反过来的,处理方式也反过来. 令$ f[n][S] $表示当前有$ S $这些点,期望这些点能够构成独立集大小为$ n $.正向的考虑选择了哪个点,并把与这个点有连边的所有点在集合内进行删除,令找到的新状态为$ f[n-1][P] $.我们把$ P $中的结点

[LOJ#2540][PKUWC2018]随机算法(概率DP)

场上数据很水,比较暴力的做法都可以过90分以上,下面说几个做法. 1. 暴力枚举所有最大独立集,对每个独立集分别DP.复杂度玄学,但是由于最大独立集并不多,所以可以拿90. 2. dp[S][k]表示考虑到排列的第k位,当前独立集为S的方案数,枚举第k+1位,根据是否与S相连转移到dp[S][k+1]或dp[S | a[k+1]][k+1].$O(n^22^n)$ 3. dp[S]表示排列的状态为S时的正确率,mx[S]表示排列状态为S时能得到的最大独立集大小,考虑转移,枚举排列里最后一个在独立

Miller_Rabin算法(随机算法,判断一个数是否是素数)

1 const int S = 20;//随机算法判定次数,S越大,判错概率越小 2 LL pow_mod(LL a, LL b, LL mod) { // a^b%mod 3 LL ans = 1; 4 a = a % mod; 5 while(b) { 6 if(b & 1) { 7 ans = (ans * a) % mod; 8 } 9 a = ( a * a ) % mod; 10 b >>= 1; 11 } 12 return ans; 13 } 14 bool check

POJ 3318:Matrix Multiplication(随机算法)

http://poj.org/problem?id=3318 题意:问A和B两个矩阵相乘能否等于C. 思路:题目明确说出(n^3)的算法不能过,但是通过各种常数优化还是能过的. 这里的随机算法指的是随机枚举矩阵C的一个位置,然后通过A*B计算是否能够得到矩阵C相应位置的数,如果不等,就直接退出了,如果跑过一定的数量后能够相等,那么就可以判断这个矩阵C等于A*B的.第一次见这样的题目...有点新奇. 暴力算法: 1 #include <cstdio> 2 using namespace std;

微信红包随机算法

最近看了一篇文章,讲微信红包随机算法的.感觉很不错,所以自己实现了下,并进行了简单测试. 算法 算法很简单,不是提前算好,而是抢红包时计算: 红包里的金额怎么算?为什么出现各个红包金额相差很大?答:随机,额度在0.01和剩余平均值*2之间. 实现 实现上述算法的逻辑主要是: public static double getRandomMoney(RedPackage _redPackage) { // remainSize 剩余的红包数量 // remainMoney 剩余的钱 if (_red

加权随机算法

加权随机算法一般应用在以下场景:有一个集合S,里面比如有A,B,C,D这四项.这时我们想随机从中抽取一项,但是抽取的概率不同,比如我们希望抽到A的概率是50%,抽到B和C的概率是20%,D的概率是10%.一般来说,我们可以给各项附一个权重,抽取的概率正比于这个权重.那么上述集合就成了: {A:5,B:2,C:2,D:1} 方法一: 扩展这个集合,使每一项出现的次数与其权重正相关.在上述例子这个集合扩展成:{A,A,A,A,A,B,B,C,C,D}然后就可以用均匀随机算法来从中选取. 好处:选取的

权重随机算法的java实现

一.概述 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的.如广告投放.负载均衡等. 如有4个元素A.B.C.D,权重分别为1.2.3.4,随机结果中A:B:C:D的比例要为1:2:3:4. 总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1).[1,3).[3,6).[6,10).然后随机出一个[0,10)之间的随机数.落在哪个区间,则该区间之后的元素即为按权重命中的元素. 实现方法

随机算法 - HNU 13348 Finding Lines

Finding Lines Problem's Link: http://acm.hnu.cn/online/?action=problem&type=show&id=13348&courseid=0 Mean: 给你平面上1e5个点,让你判断是否可以找到一条直线,使得p%的点都在这条直线上. analyse: 经典的随机算法题. 每次随机出两个点,然后对n个点进行判断,看是否有p%的点在这条直线上. 关于随机算法正确性的证明: 每次随机一个点,这个点在直线上的概率是p,p最小为2

由抽奖软件想到的随机算法总结

整整三年没更新博客了,今天和女友聊天,聊到了博客,就回来看看. 最近接触到抽奖软件,下载的源码是http://download.csdn.net/detail/ghz_sd/6918125,在这里为开源软件作出贡献的人致敬,这个软件的作者a米山,是个非常好的人,耐心的帮我调试,他的算法很简单,就是纯粹的random,用的rand()函数,我给他提了个需求,写一个作弊类,实现的功能是:指定人的中奖概率提高,配置文件类似于这样: <xml> <win> <name='a' pro