2021牛客暑期多校训练营10 F. Train Wreck(括号序转树/优先队列)详细题解
链接:https://ac.nowcoder.com/acm/contest/11261/F
来源:牛客网
题目描述
You are now the coordinator for the Nonexistent Old Railway Station! The Nonexistent Old Railway Station only has one track with a dead end, which acts as a stack: everytime you either push a train into the station, or pop the most recently added train out of the station.
Everyday, n trains gets push into and pop out of the station. The inspector has already decided today's sequence of pushing'' and
popping''. You now have a list of the
colored trains and have to assign each train to one ``pushing'' in inspector's sequence.
Meanwhile, the inspector also requires you to make the sequence of trains remaining on the track unique every time you push a train onto it. Two sequence of trains are considered different if the length is different or the i-th train in the two sequences have a different color.
Output a solution or decide that it is impossible.
输入描述:
The first line contains one integer nn(1≤n≤1061≤n≤106), indicating the number of trains.
The second line contains a bracket sequence of length 2n2n, with each ``('' indicating one ``pushing'' and ``)''indicating one ``popping''. The input guarantees that the sequence is always valid and balanced.
The third line contains n numbers kiki (1≤ki≤n1≤ki≤n), indicating the color of the ii-th train.
输出描述:
If there is no solution, output ``NO'' in one line.
Otherwise, output ``YES'' in the first line, and nn intergers in the second line, indicating the color of the ii-th train that is being pushed.
题目大意是给定一个进出栈的序列,要求为这个序列的每个元素着色,使得每一次入栈操作发生后的栈内序列是两两不同的。
首先考虑整个过程,对于每个入栈位置(即左括号位置),求出来此时栈内的元素个数。可以发现得到的元素个数序列是由多个连续的单调不减子序列构成,且每个子序列的两个值之间相差1。比如对于(())()((()())) ,得到的序列就是1 2 1 1 2 3 3,可以被划分为(1 2)(1)(1 2 3 3),注意这里的1要单独拿出来。这时就会发现,每个子序列里必定只有一个1,然后会有若干个2,每个2后面会有若干个3....不妨从简单的考虑,如果一个序列中只有一个1和若干个2,那么这若干个2肯定不能同色(显然),同样对于每个2,如果后面仅连着若干个3,那么这些3也不能同色....这样就会发现这其实是树形结构(和题解思路一样,每个1是根,若干个2是1的儿子,若干个3是这些2的儿子...),那么按照上面的结论,可以得到一个贪心的做法:对于每棵子树的根节点x,设其有sz[x]个儿子,那么用当前剩余数量最多的sz[x]种颜色将其儿子染色即可。由于若干个1作为根不好处理,不妨用一个虚拟的0节点作为树根连接这若干个1,dfs的时候只需要对0节点dfs即可。找当前剩余数量最多的sz[x]种颜色可以将颜色和剩余数量打包成结构体放进优先队列里按照剩余数量从大到小排序,每次pop出前sz[x]个颜色进行着色,如果着色完某种颜色的数量不为0,则将其再插入优先队列。如果某次没法取出sz[x]种颜色则答案为NO。
建树等过程见代码。
#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define N 4000005
#define pii pair<int,int>
#define ll long long
using namespace std;
string s;
int n;
int seq[N], cnt = 0, num = 0;
int ans[N];
struct Color {
int color, num;
bool operator < (Color o) const {
return num < o.num;
}
} c[N];
vector<int> rt;
int head[N], ver[2 * N], Next[2 * N], tot = 0;
int sz[N];//表示节点对应的儿子个数
void add(int x, int y) {
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
int pos[N];
bool ok;
priority_queue<Color> q;
void dfs(int x) {
if(!ok) return;
vector<Color> v;
for(int i = 1; i <= sz[x]; i++) {//取出sz[x]个颜色
if(!q.size()) {
ok = 0;
return;
}
Color tmp = q.top();
q.pop();
v.pb(tmp);
}
int cnt = 0;
for(int i = head[x]; i; i = Next[i]) {//先着色再继续dfs
int y = ver[i];
ans[y] = v[cnt].color;
v[cnt].num--;//使用了一个这个颜色
if(v[cnt].num) {//如果还剩下 继续入队
q.push(v[cnt]);
}
cnt++;
}
for(int i = head[x]; i; i = Next[i]) {
int y = ver[i];
dfs(y);
}
}
signed main() {
int n;
cin >> n;
cin >> s;
for(int i = 1; i <= n; i++) {
c[i].color = i;
}
for(int i = 1; i <= n; i++) {
int tmp;
cin >> tmp;
c[tmp].num++;
}
for(int i = 1; i <= n; i++) {
if(c[i].num >= 1) {
q.push(c[i]);
}
}
int len = s.size();
ok = true;
pos[0] = 0;
for(int i = 1; i <= len; i++) {
if(s[i - 1] == '(') {
num++;
seq[i] = num;
pos[num] = i;//pos[num]为前一个num所在的位置
add(pos[num - 1], i);//连边,注意节点实际上是左括号在字符串的位置+1
sz[pos[num - 1]]++;
} else {
num--;
}
}
dfs(0);//虚拟节点
if(!ok) puts("NO");
else {
puts("YES");
for(int i = 1; i <= len; i++) {
if(ans[i]) cout << ans[i] << " ";
}
cout << endl;
}
return 0;
}
// 7
// (())()((()()))
// 1 2 2 3 3 3 3