【agc008d】kth Solution Summary Code

  给你一个长度为$N$的整数序列$Xi$,请判断是否存在一个满足下列条件的整数序列$a$,如果存在,请构造一种方案

  条件如下:

  1. $a$的长度为$N^2$,并且满足数字$1,2,3…N$都各出现恰好$N$次
  2. 对于$1 le i le N$,数字$i$在$a$中第$i$次出现的位置是$X_i$

  $1 le N le 500, ;1le X_ile N^2$,保证$X_i$互不相同

Solution

  转化一下问题。题目相当于对于每个$i$,规定在$[1,x_i)$要放$i-1$个$i$,在$(x_i,n^2]$要放$n-i$个$i$

  我们称前者为“向前”的请求,后者为“向后”的请求

  这样,我们就得到了$2n$个有范围和次数限制的请求。我们可以将它们看做各自独立的请求。

  考虑从左往右枚举每一个位置$p=1…n^2$,并确定当前位置应该用来填入哪一个请求(或者说对其贡献)

  首先,观察考虑对象的变化:当前位置右移时,能填入的向前请求越来越少,而能填入的向后请求越来越多

  假设我们对于当前可用的向前的请求,按其结束位置(右端点$x_i-1$)从小到大排序,显然当前位置$p$应该填入结束位置最靠左的那一个请求,记其为$A$;如果$p$填入的请求不是$A$,而是结束位置更靠右的请求,我们完全可以填入$A$,且情况一定不会更劣,因为我们从左到右填时,优先填入了那些剩余周旋空间更小的请求

  那么我们的策略就是:如果当前有可用的向前请求,将当前位置填入结束位置最靠左的那一个。如果填完后该请求已满,则删去这个请求。

  当前位置右移时,弹出的显然是结束位置最靠左的请求,如果此时其还未被满足,则无解

  那么向后请求怎么办呢?我们发现,在考虑向前请求时之所以要优先分配给最靠左的请求,是因为最靠左的元素在最近的将来就要失去考虑机会;而对于向后的请求,随着枚举位置的右移,机会反而越来越多。因此,我们就不需要给向后请求太多的关照。当且仅当没有向前请求时,我们再随便给一个当前可考虑的向后请求填入$p$即可

  整体算法可以用一个数组和界限指针模拟

Summary

  感觉无法很好地设置状态、刻画阶段的题目,有可能就是贪心了

  线性贪心,一般是从左往右扫,优先考虑、调配和满足那些所剩考虑余地更小的元素(隐形前提是,这些考虑余地和从左往右扫呈线性相关)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

#include <algorithm>
#define END {puts("No"); return 0;}
using namespace std;
const int N=510;
int n;
int a[N];
int who[N];
int ans[N*N];
struct {
  int id,rest;
}s1[N*N],s2[N*N];
int s1cnt,s2cnt;
void readData(){
  scanf("%d",&n);
  for(int i=1;i<=n;i++){
    scanf("%d",&a[i]);
    ans[a[i]]=i;
  }
}
bool cmpByA(const int &u,const int &v){
  return a[u]<a[v];
}
bool init(){
  for(int i=1;i<=n;i++) who[i]=i;
  sort(who+1,who+1+n,cmpByA);
  for(int i=1;i<=n;i++){
    int x=who[i];
    if(x-1>a[x]-1||n-x>(n*n)-a[x])
      return false;
    if(x>1)
      s1[++s1cnt]=(Set){x,x-1};
    if(x<n)
      s2[++s2cnt]=(Set){x,n-x};
  }
  return true;
}
bool solve(){
  int b1=1,b2=1,c1=1,c2=1;
  int m=n*n;
  for(int i=1;i<=m;i++)
    if(!ans[i]){
      while(c1<=s1cnt&&a[s1[c1].id]-1<i){
        if(s1[c1].rest)
          return false;
        c1++;
      }
      while(b2<=s2cnt&&a[s2[b2].id]+1<=i)
        b2++;
      if(c1>s1cnt&&c2>=b2)
        return false;
      if(c1<=s1cnt){
        ans[i]=s1[c1].id;
        s1[c1].rest--;
        if(!s1[c1].rest)
          c1++;
      }
      else{
        ans[i]=s2[c2].id;
        s2[c2].rest--;
        if(!s2[c2].rest)
          c2++;
      }
    }
  return true;
}
int main(){
  readData();
  if(!init())
    END;
  if(!solve())
    END;
  puts("Yes");
  for(int i=1;i<=n*n;i++) printf("%d ",ans[i]);
  return 0;
}