agc046_e Permutation Cover
agc046_e Permutation Cover
https://atcoder.jp/contests/agc046/tasks/agc046_e
Tutorial
https://img.atcoder.jp/agc046/editorial.pdf
称序列中每个元素都在某个作为排列的区间中为性质1
考虑什么时候才有解.
对于单独两个元素\(a_x,a_y\)分析,若\(a_x>2a_y\),那么发现一定存在某个\(x\),距离左右的边界或\(x\)之间都没有\(y\).
也就是说对于最大值\(a_x\)和最小值\(a_y\),\(a_x \le 2a_y\)是有解的必要条件.
考虑如果满足这个条件如何构造一组解,发现对于任意\(S\in[K]\),一定存在一种序列使得\(S\)中的元素出现了二次,\([K]\setminus S\)的元素出现了一次,且满足性质1.那么就可以这样分为若干组以上面的条件为指导构造出一组解来.
所以\(a_x \le 2a_y\)是有解的充要条件.
考虑贪心的增量构造,设现在的序列为\(P\),且\(|P|\ge K\),满足性质1.考虑怎样的\(P\)是合法(即可以成为某个解的前缀)的.
和上面类似的考虑,设\(b_i\)表示剩下的序列中还需要\(i\)出现多少次,而且由于\(P\)的存在相当于左边界的条件有所改变,所以
- 设\(x,y\)分别为\(b\)的最大值和最小值,则\(x\le 2y+1\)
- 若\(x=2y+1\),则\(P\)的结尾的\(K\)个元素,称其为排列\(Q\),满足所有\(b_i=x\)的元素在\(b_i=y\)的元素之前
证明必要和充分的过程和上面类似.
此时由于我们需要\(P\)时刻满足性质1,所以不能像一般增量法一样一个个添加元素.我们枚举添加的元素个数\(m\),我们要添加的元素要和\(P\)结尾的\(K-m\)个元素组成排列,且需要满足上述\(P\)合法的条件.对于每个\(m\),我们可以贪心得到字典序最小的增加的元素的排列或判断其无解.然后对于合法的所有排列,选择字典序最小的加入\(P\).
对于每个\(m\),贪心的复杂度是\(O(K)\),总时间复杂度\(O(K^2 \sum a_i)\)
Code
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char gc() {
return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
x=0; int f=1,ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
x*=f;
}
const int maxK=100+5,maxn=1000+5;
int n,K,a[maxK],an[maxn];
bool vis[maxK];
inline bool cmp(const vector<int> &a,const vector<int> &b) {
for(int i=0;i<a.size()&&i<b.size();++i) if(a[i]!=b[i]) return a[i]<b[i];
return a.size()<b.size();
}
void fail(vector<int> &re) {
for(int i=0;i<=K;++i) re.push_back(K+1);
}
void sol(int s,int m,vector<int> &re) {
static int b[maxK]; memcpy(b,a,sizeof(b));
memset(vis,0,sizeof(vis));
for(int i=1;i+m<=K;++i) vis[an[s-i]]=1;
for(int i=1;i<=K;++i) if(!vis[i]) {
if(!b[i]) {fail(re); return;}
--b[i];
}
int x=*max_element(b+1,b+K+1),y=*min_element(b+1,b+K+1);
if(x>2*y+1) {fail(re); return;}
vector<int> A,B,C;
if(x==2*y+1) {
bool flag=0;
for(int i=1;i+m<=K;++i) {
if(b[an[s-i]]==x) flag=1;
else if(b[an[s-i]]==y) {
if(flag) {fail(re); return;}
}
}
for(int i=K;i>=1;--i) if(!vis[i]) {
if(b[i]==x) A.push_back(i);
else if(b[i]==y) B.push_back(i);
else C.push_back(i);
}
}
else for(int i=K;i>=1;--i) if(!vis[i]) C.push_back(i);
for(int i=1;i<=m;++i) {
if(A.size()) {
int t=C.size()?C.back():K+1;
if(A.back()<t) re.push_back(A.back()),A.pop_back();
else re.push_back(t),C.pop_back();
}
else {
int a=B.size()?B.back():K+1,b=C.size()?C.back():K+1;
if(a<b) re.push_back(a),B.pop_back();
else re.push_back(b),C.pop_back();
}
}
}
int main() {
rd(K);
for(int i=1;i<=K;++i) rd(a[i]),n+=a[i];
if(*max_element(a+1,a+K+1)>2*(*min_element(a+1,a+K+1))) {puts("-1"); return 0;}
for(int i=1;i<=n;) {
// debug("---\n");
vector<int> Q[maxK];
if(i==1) sol(i,K,Q[1]);
else {
for(int j=1;j<=K;++j) sol(i,j,Q[j]);
int k=1;
for(int j=2;j<=K;++j) if(cmp(Q[j],Q[k])) k=j;
if(k!=1) swap(Q[1],Q[k]);
}
for(int j=0;j<Q[1].size();++j) an[i+j]=Q[1][j],--a[Q[1][j]];
i+=Q[1].size();
// for(int j=1;j<i;++j) debug("%d ",an[j]); debug("\n");
}
for(int i=1;i<=n;++i) {
if(i!=1) printf(" ");
printf("%d",an[i]);
}
printf("\n");
return 0;
}