Simone and Graph Coloring(思维,线段树,二分)

题目描述

链接

Simone, a student of Graph Coloring University, is interested in permutation. Now she is given a permutation of length n n n, and she finds that if she connects each inverse pair, she will get a graph. Formally, for the given permutation, if i < j i<j i<j and a i > a j a_i>a_j ai>aj , then there will be an undirected edge between node i i i and node j j j 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 T T ( 1 ≤ T ≤ 1 0 6 1\leq T\leq 10^6 1T106), indicating the number of test cases.

For each test case, the first line contains an integer n n n ( 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1n106), indicating the length of the permutation.

The second line contains n n n integers a 1 a_1 a1, a 2 a_2 a2,…, a n a_n an , indicating the permutation.

It is guaranteed that the sum of n n n over all test cases does not exceed 1 0 6 10^6 106.

输出描述:

For each test case, the first line contains an integer c c c, the chromatic number(the minimal number of colors been used when coloring) of the graph.

The second line contains n n n integers c 1 c_1 c1, c 2 c_2 c2,…, c n c_n cn , the color of each node.

Notice that c i c_i ci should satisfy the limit that 1 ≤ c i ≤ c 1 \leq c_i \leq c 1cic .

If there are several answers, it is acceptable to print any of them.

输入

2
4
1 3 4 2
2
1 2

输出

2
1 1 1 2 
1
1 1

方法一

如果给任意一个无向图,完成染色操作,那就不容易做了。主要的方向还是需要从逆序对入手。

做法为:首先将 [ 0 , 1 ] [0,1] [0,1] 所有结点初始染色为 0 0 0 ,然后从 n n n 1 1 1 倒序处理,对于每个数字 i i i ,求出下标 [ 1 , i ] [1,i] [1,i] 区间的最大染色值 m x mx mx,当前结点染为 m x + 1 mx+1 mx+1

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
struct S{ int l,r,v; }t[N<<2];
int T,n,a[N],pos[N],b[N];

void build(int p,int l,int r){
	t[p].l=l,t[p].r=r,t[p].v=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(p<<1,l,mid),build(p<<1|1,mid+1,r);
}

int ask(int p,int l,int r){
	if(t[p].l>=l&&t[p].r<=r){ return t[p].v; }
	int mid=(t[p].l+t[p].r)>>1;
	int mx=0;
	if(l<=mid) mx=max(mx,ask(p<<1,l,r));
	if(r>=mid+1) mx=max(mx,ask(p<<1|1,l,r));
	return mx;
}

void change(int p,int x,int v){
	if(t[p].l==t[p].r) return (void)(t[p].v=v);
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid) change(p<<1,x,v);
	else change(p<<1|1,x,v);
	t[p].v=max(t[p<<1].v,t[p<<1|1].v);
}

void solve(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],pos[a[i]]=i;
	build(1,1,n);
	for(int i=n;i>=1;i--){
		b[pos[i]]=ask(1,1,pos[i])+1;
		change(1,pos[i],b[pos[i]]);
	}
	cout<<*max_element(b+1,b+1+n)<<"\n";
	for(int i=1;i<=n;i++) cout<<b[i]<<" \n"[i==n];
}

int main(){
	ios::sync_with_stdio(false);
	for(cin>>T;T;T--) solve();
}

方法二

对于序列中第 i i i 个数字,设下标 [ 0 , i − 1 ] [0,i-1] [0,i1] 的序列中,最长下降子序列的长度为 l e n len len ,那么当前结点染色为 l e n + 1 len+1 len+1

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,n,a[N],b[N],ls[N];

int get(int l,int r,int x){
	while(l<r){
		int mid=(l+r)>>1;
		if(ls[mid]>x) l=mid+1;
		else r=mid;
	}
	return l;
}

void solve(){
	int tot=0; cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		if(tot==0||a[i]<ls[tot]) ls[b[i]=++tot]=a[i];
		else ls[b[i]=get(1,tot,a[i])]=a[i];
	}
	cout<<tot<<"\n";
	for(int i=1;i<=n;i++) cout<<b[i]<<" \n"[i==n];
}

int main(){
	ios::sync_with_stdio(false);
	for(cin>>T;T;T--) solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_51864047

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值