LOJ2540 「PKUWC2018」随机算法(状态压缩)

传送门:https://loj.ac/problem/2540


s o l u t i o n solution solution:
直接状压 d p dp dp
f s , i f_{s,i} fs,i表示独立集的状态为 s s s,考虑到排列的第 i i i
枚举下一位转移

考虑下一位可以填什么
考虑现在可以加入独立集的点,之前肯定没考虑过,直接转移到 f s ∪ x , i + 1 f_{s∪{x},i+1} fsx,i+1
不可以加入独立集的点我们并不关心具体是哪个,只关心个数,直接转移到 f s , i + 1 f_{s,i+1} fs,i+1
复杂度 O ( 2 n ∗ n 2 ) O(2^n*n^2) O(2nn2)
把跑不到的状态直接剪掉就能卡过去了-.-

另有多记录一个东西后压掉一维的 O ( 2 n ∗ n ) O(2^n*n) O(2nn)做法

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(flag) return sum;
    else return -sum;
}
const int p = 998244353;
int n,m,ms;
int ans,mx;
int bin[25],fail[1100000],num[1100000],v[1100000],trans[1100000];
int f[1100000][20];
bool flag[1100000];
int Pow(int a,int x)
{
	int now = 1;
	while(x)
	{
		if(x&1) now = 1ll*now*a%p;
		a = 1ll*a*a%p;
		x >>= 1;
	}
	return now;
}
void init()
{
	n = rd();m = rd();
	ms = (1<<n)-1;
	rep(i,0,n) fail[bin[i-1]] = bin[i-1];
	rep(i,1,m)
	{
		int x = rd(),y = rd();
		fail[bin[x-1]] |= bin[y-1];
		fail[bin[y-1]] |= bin[x-1];
	}
	flag[0] = true;
	rep(s,0,ms) if(flag[s])
	    rep(j,1,n) if((!(s>>j-1&1)) && (!(bin[j-1]&fail[s])))
	    {
	    	flag[s|bin[j-1]] = true;
	    	num[s|bin[j-1]] = num[s] + 1;
	    	fail[s|bin[j-1]] = fail[s]|fail[bin[j-1]];
	    	mx = max(mx,num[s|bin[j-1]]);
	    }
	rep(s,0,ms)
	{
		int to = (~(fail[s]))&ms;
		trans[s] = to;
		while(to) to -= to&-to,v[s]++;
	}
}
int inv[30];
void Dp()
{
	f[0][0] = 1;
	int ans = 0;
	rep(i,1,25) inv[i] = Pow(i,p-2);
	rep(s,0,ms) rep(i,0,n)
	    if(f[s][i])
	    {
	    	if(num[s] == mx && i == n) (ans += f[s][i])%=p;
	    	f[s][i] = 1ll*f[s][i]*inv[n-i]%p;
	    	for(int to = trans[s];to;to -= to&-to)
	    	{
	    		int k = to&-to;
	    		(f[s|k][i+1] += f[s][i])%=p;
	    	}
	    	(f[s][i+1] += (1ll*f[s][i]*(n-i-v[s])%p))%=p;
	    }
    printf("%d\n",ans);
}
int main()
{
	bin[0] = 1;rep(i,1,21) bin[i] = bin[i-1]<<1;
	init();
	Dp();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值