第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(昆明)
题解参考链接1
题解参考链接2
题意
给出一个长度为n的排列,排列中每个逆序对之间连接一条无向边。最终构成一个无向图。现在请你给这个无向图染色,使得每个相邻的节点颜色均不相同。问最少需要多少种颜色。( n ≤ 1 e 6 n \leq 1e6 n≤1e6)
输入
2
4
1 3 4 2
2
1 2
输出
2
1 1 1 2
1
1 1
思路①-树状数组
算法:从 n~1 枚举每一个数字 i ,在排列中找出数字i左边比 i 大的数,从这些数中选出颜色最大的一个颜色
c
j
c_j
cj。则令数字 i 的颜色
c
i
=
c
j
+
1
c_i = c_j + 1
ci=cj+1即可。
理解: 之所以倒序枚举数字i,是因为这样更新排列中数字的颜色后,方便通过树状数组直接找出i左边比i大的数中颜色的最大值
CODE-One
const int N = 1e6 + 6;
int a[N], b[N], c[N];
int n;
void add(int x, int y)
{
while (x <= n)
c[x] = max(c[x], y),
x += x & -x;
}
int ask(int x)
{
int maxn = 0;
while (x)
maxn = max(maxn, c[x]),
x -= x & -x;
return maxn;
}
int main()
{
IOS;
int T; cin >> T;
while (T--)
{
cin >> n;
for (int i = 0; i <= n; i++)
b[i] = 0, c[i] = 0;
for (int i = 1; i <= n; i++)
cin >> a[i], b[a[i]] = i;
//b数组记录数字i在排列a中的下标
for (int i = n; i >= 1; i--)
{
int x = ask(b[i] - 1);
a[b[i]] = x + 1;//此时a序列已经没用了,就用直接覆盖a数组来存结果
//注意:需要按照排列的顺序记录每个数字的颜色
add(b[i], x + 1);
}
cout << ask(n) << endl;
for (int i = 1; i <= n; i++)
cout << a[i] << " ";
cout << endl;
}
return 0;
}
思路②-最长下降子序列
找出最长的下降子序列的长度即是最小的颜色数。。。原理不能完全理解。。。
CODE-Two
const int N = 1e6 + 6;
int a[N], b[N], c[N];
int n;
int main()
{
IOS;
int T; cin >> T;
while (T--)
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
int len = 0;
for (int i = n; i >= 1; i--)
{
if (a[i] > b[len])
{
b[++len] = a[i];
c[i] = len;
}
else
{
int x = lower_bound(b + 1, b + len + 1, a[i]) - b;
b[x] = a[i];
c[i] = x;
}
}
cout << len << endl;
for (int i = 1; i <= n; i++)
cout << c[i] << " ";
cout << endl;
}
return 0;
}