CF1552G 题解

Description

给定 n , k n,k n,k 以及位置集合 S 1 , S 2 , ⋯   , S k S_1,S_2,\cdots,S_k S1,S2,,Sk

定义第 i i i 轮排序如下: 将 S i S_i Si 中的值从小到大排序

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

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

Solution

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

Lemma

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


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

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

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

考虑做一个剪枝——只考虑那些有意义的位置。具体的,我们仅考虑所有在之前的排序中出现过至少一次的位置。例如, n = 40 n=40 n=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} 进行了排序,那么我们在第 2 2 2 轮排序结束前仅考虑第 2 , 3 , 5 , 6 , 7 , 8 2,3,5,6,7,8 2,3,5,6,7,8 位。

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

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

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

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

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

Code

注意要特判 n = 1 n=1 n=1,答案总为 ACCEPTED。否则(即 n ≠ 1 n \neq 1 n=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;
}

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/119140766