2020武大校赛预赛C题
1. 直接借助栈、差分模拟
pre数组指的是上一个与p[i]一样大的位置对应的ans,也就是ans[pre[p[i]]]。
当前的位置要比那个ans小1,而ans[i+1]也要比ans[i]小1。
因为p[i]多出现了一次,所以上一个ans[pre[p[i]]要加1,这样就不会出现非正数。
#include <bits/stdc++.h> using namespace std; int n,p[200005],pre[200005],a[200005]; void solve() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",p+i); if(p[i]==-1) p[i]=p[i-1]+1; a[i]=0,pre[i]=i; } stack<int> q; for(int i=1;i<=n;i++) { int t=p[i-1]-p[i]+1; while(t--) pre[i]=min(pre[i],pre[q.top()]),q.pop(); q.push(i); a[i]+=pre[i]-1,a[i+1]-=pre[i],a[pre[i]]++; } for(int i=1,t=0;i<=n;i++) t+=a[i],printf("%d ",t); printf("\n"); } int main() { int _; scanf("%d",&_); while(_--) solve(); return 0; }
2. 用邻接表/链表模拟
可以根据大小的相对次序来模拟,遍历邻接表就可以得到答案。
#include <bits/stdc++.h> #define ll long long using namespace std; const int maxn = 2e5+10; int T, n; int p[maxn], a[maxn]; int tail, nxt[maxn], pre[maxn], lst[maxn]; int main() { scanf("%d", &T); while(T--) { scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &p[i]); if(p[i] == -1) p[i] = p[i-1] + 1; nxt[i] = pre[i] = 0; } tail = 0; for(int i = 1; i <= n; i++) { if(p[i] > p[i-1]) { //接在尾部 nxt[tail] = i; pre[i] = tail; tail = i; } else { //接在上一个同样大小的的前面 int x = lst[p[i]]; nxt[pre[x]] = i; pre[i] = pre[x]; nxt[i] = x; pre[x] = i; } lst[p[i]] = i; } for(int i = 1, j = nxt[0]; i <= n; i++, j = nxt[j]) { a[j] = i; } for(int i = 1; i <= n; i++) printf("%d ", a[i]); puts(""); } return 0; }
3. 用拓扑排序
设每个位置的入度是大于p[i]的个数,则可通过拓扑排序得到答案。
借助大根堆来模拟,这个时候是位置越靠后的ans[i]越大。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<iostream> #include<vector> #include<queue> #include<map> #include<cassert> #include<functional> #include<numeric> #include<set> #include<unordered_map> #include<tuple> #include<random> using namespace std; #define rep(i,l,r) for(int i=(l);i<=(r);++i) #define rpe(i,r,l) for(int i=(r);i>=(l);--i) #define rpp(i,x,e,head) for(int i=head[x];~i;i=e[i].next) #define dyes cerr<<"yes"<<endl #define dbg(x) cerr<<#x<<"="<<x<<endl #define debug(...) fprintf(stderr, __VA_ARGS__) #define pts puts("") typedef double db; typedef long long ll; typedef unsigned long long ull; inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch=='-')f=-1LL;}while(ch<'0'||ch>'9'); do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9'); return f*x; } inline ll readll(){ ll f=1,x=0;char ch; do{ch=getchar();if(ch=='-')f=-1LL;}while(ch<'0'||ch>'9'); do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9'); return f*x; } template <class T> inline void chmax(T &a,T b){if(a<b) a=b;} template <class T> inline void chmin(T &a,T b){if(a>b) a=b;} inline void swap(int &a,int &b){int c=a;a=b;b=c;} using namespace std; #define mst(a,val) memset(a,val,sizeof(a)) #define pii pair<int,int> #define piii pair<int,pair<int,int> > #define mp(i,j) make_pair(i,j) #define fi first #define sc second #define inf (0x3f3f3f3f) #define infl (0x3f3f3f3f3f3f3f3fLL) #define forvec(i,j) for(vector<int>::iterator i=j.begin();i!=j.end();++i) #define forvecv(i,j) for(vector<int>::iterator i=--j.end();i>=j.begin();--i) //=====================head end======================// const int N=2e5+10; int n,a[N],pre[N],in[N],ans[N]; vector<int> G[N]; inline void wk(){ n=read();rep(i,1,n) a[i]=read(); rep(i,1,n) G[i].clear(); rep(i,1,n) in[i]=0; rep(i,1,n) pre[i]=0; rep(i,1,n) if(a[i]==-1) a[i]=a[i-1]+1; rep(i,1,n){ if(a[i]<=a[i-1]) G[pre[a[i]]].emplace_back(i),++in[i]; if(a[i]>1) G[i].emplace_back(pre[a[i]-1]),++in[pre[a[i]-1]]; pre[a[i]]=i; } priority_queue<int> pq; rep(i,1,n) if(!in[i]) pq.emplace(i); int cur=n; while(!pq.empty()){ int x=pq.top();pq.pop(); ans[x]=cur;cur--; for(auto v:G[x]){ --in[v]; if(!in[v]) pq.emplace(v); } } rep(i,1,n) printf("%d%c",ans[i]," \n"[i==n]); } int main(){ int T=read(); while(T--) wk(); return 0; }
4. 用栈模拟大小次序,最后离散化得到答案
如果p[i[比当前栈的大小还大,则直接添加。
否则,ans[i]就是上一个和p[i]样大的位置对应的ans-1,并更新栈大小为当前ans[i]。
#include <bits/stdc++.h> using namespace std; #define int long long int n, a[1000005], top; int b[1000005], ans[1000005], srt[1000005], mx; signed main() { int t; cin>>t; while(t--){ cin>>n; for (int i = 1; i <= n; i++) { cin>>a[i]; ans[i]=0; b[i]=0; srt[i]=0; } int top=0; int mx=0; for (int i = 1; i <= n; i++) { if (a[i] == -1) { ans[i] = i * 1000005; b[++top] = ans[i]; } else { if (a[i] > top) { ans[i] = i * 1000005; b[++top] = ans[i]; } else { ans[i] = b[a[i]] - 1; b[top = a[i]] = ans[i]; } } } for (int i = 1; i <= n; i++) srt[i] = ans[i]; sort(srt + 1, srt + n + 1); for (int i = 1; i <= n; i++) ans[i] = lower_bound(srt + 1, srt + n + 1, ans[i]) - srt; for (int i = 1; i <= n; i++) cout<<ans[i]<<" "; cout<<endl; } return 0; }
5. 用dfs,根据p[i]的值一层层处理。
首先将对应p[i]位置存起来,然后根据题解描述,找出分段,并递归处理分段。
分段的区间可以通过二分找到,不用再遍历了。
#include <bits/stdc++.h> #define ll long long using namespace std; const int maxn = 1e5+10; int t, n, num, top; int a[maxn], b[maxn], sta[maxn]; vector<int> v[maxn]; void dfs(int pos, int l, int r) { // printf("%d %d %d %d\n", pos, num, l, r); if(l > r || v[pos].size() == 0) { return ; } int st = lower_bound(v[pos].begin(), v[pos].end(), l) - v[pos].begin(); int ed = lower_bound(v[pos].begin(), v[pos].end(), r) - v[pos].begin(); /*printf("%d %d %d\n", pos, st, ed); for(int i = 0; i < (int)v[pos].size(); i++) printf("%d ", v[pos][i]); puts("");*/ if(st >= ed) return ; b[v[pos][ed-1]] = num++; for(int i = ed-2; i >= st; i--) { b[v[pos][i]] = num++; } dfs(pos+1, l, v[pos][st]); for(int i = st; i < ed-1; i++) { dfs(pos+1, v[pos][i], v[pos][i+1]); } dfs(pos+1, v[pos][ed-1], r); } int main() { scanf("%d", &t); while(t--) { scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); if(a[i] == -1) a[i] = a[i-1]+1; v[a[i]].push_back(i); } /*for(int i = 1; i <= n; i++) { for(int j = 0; j < (int)v[i].size(); j++) printf("%d ", v[i][j]); puts(""); }*/ num = 1; dfs(1, 0, n+1); for(int i = 1; i <= n; i++) printf("%d ", b[i]); puts(""); /*top = 0; for(int i = 1; i <= n; i++) { while(top > 0 && sta[top] > b[i]) top--; sta[++top] = b[i]; printf("%d ", top); } puts("");*/ for(int i = 1; i <= n; i++) v[i].clear(); } return 0; }