2020武大校赛预赛C题

Posted canchan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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("
");
}
 
int main()
{
    int _;
    scanf("%d",&_);
    while(_--) solve();
    return 0;
}
View Code

 

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;
}
View Code

 

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]," 
"[i==n]);
}
int main(){
    int T=read();
    while(T--) wk();
    return 0;
}
View Code

 

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;
}
View Code

 

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
", 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
", 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;
}
View Code

 

以上是关于2020武大校赛预赛C题的主要内容,如果未能解决你的问题,请参考以下文章

ACM-ICPC 2018 焦作赛区网络预赛 I题(滑稽)

河南工业大学校赛 C题.魔法宝石

HDU - 6513 Reverse It (SYSU校赛C题)(组合数学+容斥)

郑州轻院ACM校赛骗钱记

计蒜客 31001 - Magical Girl Haze - [最短路][2018ICPC南京网络预赛L题]

ACM-ICPC 2018 焦作赛区网络预赛 K题 Transport Ship