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