牛客多校10 Train Wreck (模拟,思维题,优先队列重载小于号的操作)

49 篇文章 1 订阅
7 篇文章 0 订阅
2021牛客暑期多校训练营10

F. Train Wreck

题意:
  • n n n 对括号, ′ ( ′ '(' ( 表示火车进站, ′ ) ′ ')' ) 表示出站

    现给出 n n n 个火车的颜色,问如何排序,才能使得 n n n 次火车进站产生的 n n n 个颜色序列两两不相同

分析:
  • 先模拟一遍自己想出来的示例 " 11 , ( ( ( ) ) ( ) ( ( ( ) ) ) ) ( ) ( ( ) ( ) ) " "11 , ((())()((())))()(()())" "11,((())()((())))()(()())"​​ ,产生 11 11 11 个序列:

    1 1 1

    1 , 2 1 , 2 1,2

    1 , 2 , 3 1 , 2 , 3 1,2,3

    1 , 4 1 , 4 1,4

    1 , 5 1 , 5 1,5

    1 , 5 , 6 1 , 5 , 6 1,5,6

    1 , 5 , 6 , 7 1 , 5 , 6 , 7 1,5,6,7

    8 8 8

    9 9 9

    9 , 10 9 , 10 9,10

    9 , 11 9 , 11 9,11

    要使 n n n 次序列两两不相同,那么

    • 1 , 8 , 9 1 , 8 , 9 1,8,9 颜色不同
    • 2 , 4 , 5 2 , 4 , 5 2,4,5 颜色不同
    • 3 , 6 3 , 6 3,6 颜色不同
    • 10 , 11 10 , 11 10,11 颜色不同

    应该发现规律了吧,第 1 1 1 列的车要完全不同,从第 2 2 2 列开始,就可以以第 1 1 1 列的颜色来分类,对每一类单独讨论即可

  • 注意一点:每次取颜色号的时候要选取剩余数量最多的 (用优先队列来处理)

#include<bits/stdc++.h>
//#define int long long
using namespace std;

typedef long long ll;
const int N=2e6+5;
struct Node 
{ 
    int x, y; // x是颜色号次, y是个数
    bool operator<(const Node &b) const{ return y < b.y; }
}tp[N];
priority_queue <Node> q; // 返回个数从大到小
vector <int> g[N]; // g[i] 里面存长度为i的序列的最后一个元素的下标
int s[N], len[N], ans[N], buc[N];
int vis[N], st[N];
bool add(int l,int r,int le)
{
    if(le<=1) return 1; 
    for(int i=2;i<=le;i++)
    {
        int tot=0;
        for(int j=st[i];j<g[i].size();j++)
        {
            if(g[i][j] >= r) {  st[i]=j; break ; }
            // st 记录一下处理过的,不然会超时
            if(g[i][j] > l)
            {
                if(q.empty()) return 0;
                tp[++tot] = q.top(); q.pop();
                ans[g[i][j]] = tp[tot].x;
                tp[tot].y -= 1;
            }
        }
        for(int j=1;j<=tot;j++)
        {
            if(tp[j].y) q.push(tp[j]);
        }
    }
    return 1;
}
signed main()
{
	//freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int n;
    cin>>n;
    int tot=0, cnt=0;
    for(int i=1;i<=n*2;i++) 
    {
        char c;
        cin>>c;
        if(c=='(')
        {
            s[i] = s[i-1]+1; // 前缀和产生序列
            g[s[i]].push_back(++cnt);
            len[tot]=max(s[i],len[tot]);
            if(s[i]==1)
            {
                len[++tot]=1;
            }
        } 
        else s[i] = s[i-1]-1;
    }
    cnt=0;
    for(int i=1;i<=n;i++) 
    {
        int x;
        cin>>x; buc[x]++;
    }
    for(int i=1;i<=n;i++)
    {
        if(buc[i])q.push(Node{i,buc[i]}); // 将颜色和个数压入优先队列
    }
    
    for(int i=0;i<tot;i++)
    {   // 处理第长度为 1 的
        if(q.empty()) { cout<<"NO"<<endl; return 0; }
        tp[i]=q.top(); q.pop();
        ans[g[1][i]] = tp[i].x; 
        tp[i].y -= 1;
    }
    for(int i=0;i<tot;i++) 
    {
        if(tp[i].y) q.push(tp[i]);
    }
    g[1].push_back(n+1); // 处理边界
    for(int i=0;i<tot;i++)
    {
        if(!add(g[1][i], g[1][i+1], len[i+1])) return cout<<"NO"<<endl, 0;
    }
    cout<<"YES"<<endl;
    for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yezzz.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值