题目描述
Simone, a student of Graph Coloring University, is interested in permutation. Now she is given a permutation of length nnn, and she finds that if she connects each inverse pair, she will get a graph. Formally, for the given permutation, if i<ji<ji<j and ai>aja_i>a_jai>aj, then there will be an undirected edge between node iii and node jjj in the graph.
Then she wants to color this graph. Please achieve poor Simone's dream. To simplify the problem, you just need to find a way of coloring the vertices of the graph such that no two adjacent vertices are of the same color and minimize the number of colors used.
输入描述:
There are multiple test cases. The first line of the input contains an integer T(1≤T≤106)T(1\leq T\leq 10^6)T(1≤T≤106), indicating the number of test cases. For each test case, the first line contains an integer n(1≤n≤106)n(1 \leq n \leq 10^6)n(1≤n≤106), indicating the length of the permutation. The second line contains nnn integers a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an, indicating the permutation. It is guaranteed that the sum of nnn over all test cases does not exceed 10610^6106.
输出描述:
For each test case, the first line contains an integer ccc, the chromatic number(the minimal number of colors been used when coloring) of the graph. The second line contains nnn integers c1,c2,...,cnc_1,c_2,...,c_nc1,c2,...,cn, the color of each node. Notice that cic_ici should satisfy the limit that 1≤ci≤c1 \leq c_i \leq c1≤ci≤c. If there are several answers, it is acceptable to print any of them.
示例1
输入
2 4 1 3 4 2 2 1 2
输出
2 1 1 1 2 1 1 1
题意: 给出一个排列,每对逆序对(i, j)连无向边,最终构成一张无向图,对每个点染色,求最少需要用到的颜色数以及各点的颜色分别是什么。
分析: 从左向右扫描一遍数组,如果在当前点i前面出现比a[i]大的数a[j],那么i与j就要连边,i的颜色也就不能等于j的颜色。对于数组中每个点存在一个集合,集合内每个点都在当前点之前且其数字大小都大于当前点数字大小,显然依照题意当前点i会与集合内每个点都连一条边,设集合中点的颜色最大为max,如果max > 1,则颜色为max的点一定至少连接一个颜色为max-1的点,否则max可以取到更优的max-1,以此类推,颜色为max-1的点一定连接一个颜色为max-2的点,直到颜色为1,由于大小关系具有传递性,当前点与颜色为max、max-1......2、1的点都会连边,因此当前点颜色只能为max+1。
这样问题就转化为在当前点数值位置更新颜色,区间查询大于当前点数值位置的所有颜色中的max,当前点的颜色就是max+1。在大于当前点数值的区间内查询,这样可以访问到出现在该点前且数值大于该点数值的所有颜色,思路类似于树状数组求逆序对的个数。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int T;
int _max[4000005], ans[1000005];
void push_up(int id)
{
_max[id] = max(_max[id<<1], _max[id<<1|1]);
}
void update(int id, int l, int r, int L, int C)
{
if(l == r)
{
_max[id] = C;
return;
}
int m = l+r>>1;
if(L <= m)
update(id<<1, l, m, L, C);
else
update(id<<1|1, m+1, r, L, C);
push_up(id);
}
int query(int id, int l, int r, int L, int R)
{
if(l == L && r == R)
return _max[id];
int m = l+r>>1;
if(m >= R)
return query(id<<1, l, m, L, R);
else if(m+1 <= L)
return query(id<<1|1, m+1, r, L, R);
else
return max(query(id<<1, l, m, L, m), query(id<<1|1, m+1, r, m+1, R));
}
signed main()
{
cin >> T;
while(T--)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= 4*n; i++)
_max[i] = 0;
for(int i = 1; i <= n; i++)
{
int t;
scanf("%d", &t);
ans[i] = query(1, 1, n, t, n)+1;
update(1, 1, n, t, ans[i]);
}
printf("%d\n", _max[1]);
for(int i = 1; i <= n; i++)
printf("%d ", ans[i]);
puts("");
}
return 0;
}