CF1552G 题解

Description

给定 n,kn,kn,k 以及位置集合 S1,S2,⋯ ,SkS_1,S_2,\cdots,S_kS1,S2,,Sk

定义第 iii 轮排序如下: 将 SiS_iSi 中的值从小到大排序

你需要判断,若依次执行第 1,2,3,⋯ ,k1,2,3,\cdots,k1,2,3,,k 轮排序,是否任意数列都能最终变成单调不下降的

1≤n≤40,1≤k≤101 \le n \le 40,1 \le k \le 101n40,1k10

Solution

下面,为方便叙述,令一个序列可以被复原,当且仅当对其依次执行第 1,2,3,⋯ ,k1,2,3,\cdots,k1,2,3,,k 轮排序后,该排列不降。

Lemma

答案为 ACCEPTED当且仅当所有 010101 序列可被复原。


考虑求出所有可被复原的 010101 串。若其仅包含形如 0000...0111...11 的串,那么答案为 ACCEPTED,否则为 REJECTED

考虑扫描每轮排序,并维护若干个当前可达010101 串。其中,若在第 iii 轮排序后串 S0′S_0'S0 可达,当且仅当存在某个初始串 S0S_0S0 使得其经历前 iii 轮排序后变为 S0′S_0'S0。于是,我们得到了算法流程: 维护若干个当前可达010101 串,每次向上向下扩展。可以理解为 BFS。

这样我们就得到了一个朴素的做法。然而,其复杂度很劣:尤其是在第 111 轮排序前,可达的 010101 串有 2n2^n2n 个,无法直接维护。

考虑做一个剪枝——只考虑那些有意义的位置。具体的,我们仅考虑所有在之前的排序中出现过至少一次的位置。例如,n=40n=40n=40,且前两轮排序分别对 {3,5,7,8}\{3,5,7,8\}{3,5,7,8}{2,5,6,7}\{2,5,6,7\}{2,5,6,7} 进行了排序,那么我们在第 222 轮排序结束前仅考虑第 2,3,5,6,7,82,3,5,6,7,82,3,5,6,7,8 位。

剪枝后,我们该如何进行扩展呢?令 SiS_iSi 为第 iii 轮排序的位集,Si′S'_iSi 为在 SiS_iSi 中出现然而没有S1,S2,⋯ ,Si−1S_1,S_2,\cdots,S_{i-1}S1,S2,,Si1 中出现的位集。显然,只需要确定了序列的 Si′S'_iSi000 的数量,我们就能唯一确定的扩展后的 010101 序列。于是我们从 000 枚举到 ∣Si′∣|S'_i|Si,计算出新的 010101 序列扩展出去即可。

考虑时间复杂度。注意到,在第 iii 轮排序中,PPP 的大小翻了 ∣Si+1′∣+1|S'_{i+1}|+1Si+1+1 倍,所以时间复杂度为

O((∣S1′∣+1)(∣S2′∣+1)(∣S3′∣+1)⋯(∣Sk′∣+1))O((|S'_1|+1)(|S'_2|+1)(|S'_3|+1)\cdots(|S'_k|+1))O((S1+1)(S2+1)(S3+1)(Sk+1))

由于所有 ∣Si′∣+1|S'_i|+1Si+1 的和不超过 n+kn+kn+k,而 Si′S'_iSi 共有 kkk 个,所以复杂度等价于——选定 kkk 个和为 n+kn+kn+k 的数使得它们的乘积最大。可以发现,其为 O((n+kk)k)O((\frac {n+k} k)^k)O((kn+k)k)

综上所述,我们采用二进制数+位运算加速转移,单次 O(1)O(1)O(1),总复杂度 O((n+kk)k)O((\frac {n+k} k)^k)O((kn+k)k)。本题被解决。

Code

注意要特判 n=1n=1n=1,答案总为 ACCEPTED。否则(即 n≠1n \neq 1n=1 的情况),若没能覆盖到所有的位置,那么答案总为 REJECTED

yysy,下面的代码非常难懂而且我不想解释,所以不建议阅读。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxl=45;

int read(){
	int s=0,w=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-')  w=-w;ch=getchar();}
	while (ch>='0'&&ch<='9'){s=s*10+(ch^'0');ch=getchar();}
	return s*w;
}
int n,k,cover_tot,flag=1,lb;
int p[maxl],s[maxl],bc_s[maxl],set01[maxl][maxl],good_list[maxl];

bool getbit(int x,int y){return x&(1ll<<(y-1));}
bool judge_state(int val){
	int p=lower_bound(good_list,good_list+n+1,val)-good_list;
	return (good_list[p]==val);
}

void dfs(int now,int state){
	if (now==k+1){
		if (!judge_state(state))  flag=0;
		return;
	}
	int occ=(p[now]^s[now]),cnt0,new_state=state;
	cnt0=__builtin_popcountll(((1ll<<n)-1-state)&occ);
	state=(state&((1ll<<n)-1-p[now]));
	for (int i=0;i<=bc_s[now];i++){
		new_state=(state|set01[now][cnt0]);
		dfs(now+1,new_state),cnt0++;
	}
}

signed main(){
	n=read(),k=read();
	if (n==1)  return puts("ACCEPTED"),0;
	for (int i=1;i<=n;i++)  good_list[i]=good_list[i-1]+(1ll<<(i-1));
	for (int i=0;i<=n;i++)  good_list[i]=(1ll<<n)-1-good_list[i];
	sort(good_list,good_list+n+1);
	
	for (int i=1;i<=k;i++){
		int len=read();
		for (int j=1;j<=len;j++){
			int x=read();
			p[i]+=(1ll<<(x-1));
		}
	}
	for (int i=1;i<=k;i++){
		int len=0,all=0;
		set01[i][len]=0;
		
		for (int j=1;j<=n;j++){
			if (getbit(p[i],j)){
				len++;
				set01[i][len]=set01[i][len-1]+(1ll<<(j-1));
				all+=(1ll<<(j-1));
			}
		}
		for (int j=0;j<=len;j++)  set01[i][j]=all-set01[i][j];
	}
	for (int i=1;i<=k;i++){
		for (int j=1;j<=n;j++){
			if (getbit(p[i],j)==1&&getbit(cover_tot,j)==0)
			  s[i]+=(1ll<<(j-1));
		}
		for (int j=1;j<=n;j++)  bc_s[i]+=getbit(s[i],j);
		cover_tot|=p[i];
	}
	if (cover_tot!=((1ll<<n)-1))  return puts("REJECTED"),0;
	
	dfs(1,0);
	if (flag==0)  puts("REJECTED");
	else puts("ACCEPTED");
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值