题目传送门:https://www.nowcoder.com/acm/contest/142/J
题意:给一个hash table,求出字典序最小的插入序列,或者判断不合法。
分析:
eg.对于序列{7,8,16},插入后为{16, -1, -1, -1, -1, -1, -1, 7, 8}。(即,依次插入7,8,16。而插入16时发现7已经被占,所以依次考虑(h(x)+1)%n ,因此16放在0的位置上。)这是正向插入,问题是给一个最终序列,问插入序列。
通过对hash表的观察可以得到:
一个区间里的数一定比单独一个数插入更早。
1.存在答案:
那么就会存在对于每一个数a[i],存在两个位置s=a[i]%n,即本应该放在的位置。和t=i,即实际放在的位置。
朴素O(n^2)建图:
每找到一个数字s!=t,那么向前建边。若s==t,不需要建边。然后每次选择度数为0的点中最小的数(保证字典序min),然后把与该点相连的所有边删去。
通过上述朴素建图,发现每个点所建边的是一段区间内的所有点。引出:
线段树O(n)优化建图:
同上,若一个点需要连向一个区间,就向线段树上的这个区间连边。然后把度数为0的点一一删去,即把线段树内对应的叶子节点清空,然后向上更新。当一个区间为空时,就把所有连向这个点的所有边删掉了。
ps.一个点最多会向线段树上logn个区间,即logn个点相连。
2.判断不合法:
在区间[s,t)内存在-1,即序列不合法。
或拓扑排序出现环。
1 #include <bits/stdc++.h> 2 #define maxn 200005 3 using namespace std; 4 typedef pair<int,int> pii; 5 int fix[maxn],a[maxn]; 6 int pos[maxn<<2],id[maxn<<2]; 7 int deg[maxn<<2]; 8 vector<int> G[maxn<<2]; 9 int n; 10 void init(int n){ 11 memset(deg,0,sizeof(int)*(n<<2|5)); 12 for (int i=0;i<(n<<2|5);i++) G[i].clear(); 13 } 14 void addedge(int u,int v){ 15 G[u].push_back(v); 16 deg[v]++; 17 } 18 void build(int root,int l,int r){ 19 id[root]=-1; 20 if (l==r){ 21 pos[l]=root; 22 id[root]=l; 23 return ; 24 } 25 int mid=(l+r)/2; 26 addedge(root<<1,root); 27 addedge(root<<1|1,root); 28 build(root<<1,l,mid); 29 build(root<<1|1,mid+1,r); 30 } 31 int judge(int s,int t){ 32 if (s<=t) return (fix[t]-fix[s]==0) && (a[s]!=-1); 33 else return (fix[n-1]-fix[s-1]==0) && !fix[t]; 34 } 35 void Addedge(int L,int R,int p,int root,int l,int r){ 36 if (L<=l && r<=R){ 37 addedge(root,p); 38 return ; 39 } 40 int mid=(l+r)/2; 41 if (L<=mid) Addedge(L,R,p,root<<1,l,mid); 42 if (R>mid) Addedge(L,R,p,root<<1|1,mid+1,r); 43 } 44 void topo(){ 45 priority_queue<pii,vector<pii>,greater<pii> > Q; 46 for (int i=0;i<n;i++) 47 if (!deg[pos[i]]) Q.push({a[i],pos[i]}); 48 vector<int> ans; 49 while (!Q.empty()){ 50 pii pos=Q.top(); 51 Q.pop(); 52 int u=pos.second; 53 if (pos.first!=-1) ans.push_back(pos.first); 54 int v; 55 for (auto v : G[u]){ 56 if (--deg[v]==0){ 57 if (id[v]==-1) Q.push({-1,v}); 58 else Q.push({a[id[v]],v}); 59 } 60 } 61 } 62 if (ans.size()!=n-fix[n-1]){ 63 cout << -1 << endl; 64 return ; 65 } 66 if (ans.size()==0){ 67 cout << endl; 68 return ; 69 } 70 for (int i=0;i<ans.size();i++){ 71 if (i==0) cout << ans[i]; 72 else cout << " " << ans[i]; 73 } 74 cout << endl; 75 return ; 76 } 77 int main(){ 78 int t; 79 cin >> t; 80 while (t--){ 81 cin >> n; 82 init(n); 83 for (int i=0;i<n;i++){ 84 cin >> a[i]; 85 if (i) fix[i]=fix[i-1]+(a[i]==-1); 86 else fix[i]=(a[i]==-1); 87 } 88 int flag=0; 89 for (int i=0;i<n;i++){ 90 if (a[i]!=-1 && !judge(a[i]%n,i)){ 91 cout << -1 << endl; 92 flag=1; 93 break; 94 } 95 } 96 if (flag) continue; 97 build(1,0,n-1); 98 for (int i=0;i<n;i++){ 99 if (a[i]!=-1){ 100 int s=a[i]%n,t=i; 101 if (s==t) continue; 102 if (s<t){ 103 Addedge(s,t-1,pos[i],1,0,n-1); 104 } 105 else{ 106 if (t) Addedge(0,t-1,pos[i],1,0,n-1); 107 Addedge(s,n-1,pos[i],1,0,n-1); 108 } 109 } 110 } 111 topo(); 112 } 113 return 0; 114 }