2020icpc昆明站 Simone and graph coloring(线段树)

题目链接:L-Simone and graph coloring_第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(昆明) (nowcoder.com)

题意:给定一个 n 的排列,每两个点直接如果存在逆序就连一条边。请你对这 n 个点染色,要求任意一条边的两点颜色不同,且使用的颜色数量最少,输出染色方案。

分析:先来拿一组样例来模拟一下吧

对于样例 1 3 4 2来说,1可以染成1,3和4因为前边没有数字与其有连边,所以3和4也可以连1,而2前面有3和4与其均有连边,所以2的颜色不能与3和4的颜色相同,可以选择所有与2有连边的点中的颜色的最大值+1作为2即将染的颜色,这就转化为了一个逆序对问题,假如当前要对第i个点染色,因为只有逆序对之间会有连边,所以我们就根据i之前且与i构成逆序对的点的颜色情况来决定对i染的颜色,那i之前且与i构成逆序对的点的颜色情况是怎么分布的呢?假如i之前且与i构成逆序对的点的颜色中最大值是maxx,则i之前且与i构成逆序对的点的颜色一定包含了1~maxx所有值,若对于j<maxx,不存在i之前且与i构成逆序对的点的颜色为j,那么就不可能有j+1,因为没有j,则颜色为j+1的点一定可以取到j,所以i之前且与i构成逆序对的点的颜色一定包含了1~maxx所有值。我们只需要查找到i之前且与i构成逆序对的点中颜色的最大值maxx,我们就可以得到对i染的颜色为maxx+1。这样我们就可以用类似于线段树求逆序对的方法来解决这道题目了。从前往后加点,假如当前已经加完了i-1个点,对于我们当前所要加的点a[i],我们先查找a[i]~n中颜色的最大值,然后就可以确定第i个点的最大值了。

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=4e6+10;
int a[N],l[N],r[N],sum[N],tans[N];
void pushup(int id)
{
	sum[id]=max(sum[id<<1],sum[id<<1|1]);
}
void build(int id,int L,int R)
{
	l[id]=L;r[id]=R;sum[id]=0;
	if(L==R) return ;
	int mid=L+R>>1;
	build(id<<1,L,mid);
	build(id<<1|1,mid+1,R);
}
void update_point(int id,int x,int val)
{
	if(l[id]==r[id])
	{
		sum[id]=val;
		return ;
	}
	int mid=l[id]+r[id]>>1;
	if(x<=mid) update_point(id<<1,x,val);
	else update_point(id<<1|1,x,val);
	pushup(id);
}
int query_interval(int id,int L,int R)
{
	if(l[id]>=L&&r[id]<=R) return sum[id];
	int mid=l[id]+r[id]>>1;
	int ans=0;
	if(mid>=L) ans=max(ans,query_interval(id<<1,L,R));
	if(mid+1<=R) ans=max(ans,query_interval(id<<1|1,L,R));
	return ans;
}
int main()
{
	int n,T;
	cin>>T;
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		build(1,1,n);
		for(int i=1;i<=n;i++)
		{
			tans[i]=query_interval(1,a[i],n)+1;
			update_point(1,a[i],tans[i]);
		}
		int maxx=0;
		for(int i=1;i<=n;i++) maxx=max(maxx,tans[i]);
		printf("%d\n",maxx);
		for(int i=1;i<=n;i++) printf("%d ",tans[i]);
		puts("");
	}
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值