[线段树][思维]Simone and graph coloring 第45届icpc区域赛昆明站L

59 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值