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 101≤n≤40,1≤k≤10
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,⋯,Si−1 中出现的位集。显然,只需要确定了序列的 Si′S'_iSi′ 中 000 的数量,我们就能唯一确定的扩展后的 010101 序列。于是我们从 000 枚举到 ∣Si′∣|S'_i|∣Si′∣,计算出新的 010101 序列扩展出去即可。
考虑时间复杂度。注意到,在第 iii 轮排序中,PPP 的大小翻了 ∣Si+1′∣+1|S'_{i+1}|+1∣Si+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|+1∣Si′∣+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;
}
279

被折叠的 条评论
为什么被折叠?



