题目链接:https://codeforces.com/contest/1213/problem/F
解题思路:
直接让a[i] 指向 a[i + 1], 表示的意思是字母大小小的指向大的;所以我们形成环的所有下标的位置必须是相同的字母,所以我们可以直接使用 tarjan算法,判断环的个数ans, 如果ans < k,则直接输出NO,否则输出YES。
注意:
题目中要求我们输出的是从1 - n输出对应的字母,而不是按照给出的序列输出对应的字母, 比赛的时候,我就是因为输出弄错了没过这道题。
代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <stack>
using namespace std;
const int N = 2e5 + 10, M = 6e5 + 5;
int n, k, num, cnt;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], c[N], ins[N], a[N];
stack<int> st;
vector<int> ssc[N];
char str[N];
inline void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
inline void tarjan(int x) {
dfn[x] = low[x] = ++ num;
st.push(x); ins[x] = 1;
for(int i = h[x]; i + 1; i = ne[i]) {
int v = e[i];
if(!dfn[v]) {
tarjan(v);
low[x] = min(low[x], low[v]);
} else if(ins[v]) {
low[x] = min(low[x], dfn[v]);
}
}
if(low[x] == dfn[x]) {
++ cnt; int y;
do {
y = st.top(); st.pop(); ins[y] = 0;
c[y] = cnt; ssc[cnt].push_back(y);
} while(x != y);
}
}
int main(void) {
// freopen("in.txt", "r", stdin);
scanf("%d%d", &n, &k);
memset(h, -1, sizeof h);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
if(i > 1) add(a[i - 1], a[i]);
}
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
if(i > 1) add(a[i - 1], a[i]);
}
for(int i = 1; i <= n; i ++)
if(!dfn[i]) tarjan(i);
if(cnt < k) puts("NO");
else {
puts("YES");
char tmp = 'a';
int pre;
for(int i = 1; i <= n; i ++) {
if(i == 1) {
str[a[i]] = tmp;
pre = c[a[i]];
} else {
if(pre == c[a[i]]) str[a[i]] = tmp;
else {
if(tmp < 'z') tmp += 1;
str[a[i]] = tmp;
pre = c[a[i]];
}
}
}
for(int i = 1; i <= n; i ++)
printf("%c", str[i]);
puts("");
}
return 0;
}
总结:审题要仔细,在动手之前起码得把要输出什么弄清楚。
注意 tarjan算法中直接从1 - n去扫面。