2018-2019 ACM-ICPC, Asia Xuzhou Regional Contest H. Rikka with A Long Colour Palette 贪心

题目链接:https://codeforces.com/gym/102012
题意不再赘述。
这里看到海大过了,感觉是个不难的题,也想到了贪心,但是后面求方案卡住了。感觉是自己状态有问题吧,后面其实应该想出来的,题解有的都想到过,但是就是整合不到一起。
首先,可以肯定,如果一段区间被覆盖超过K次一定会满足题意。证明不会,但是跑了几组复杂一点的数据都没卡住。
然后想怎么找方案数。很显然需要离散化一下,离散化之后,我们考虑贪心的填颜色。每次只要这个区间还没被所有颜色覆盖,就填一种不同的颜色,具体实现方法是用一个q_col队列,队列里存的都是当前没填的颜色。剩下的就都是细节了。
细节:
1:如果遇到一个左端点,但是当前区间已经被全覆盖了,那么我不能乱填,因为这个填色有后效性,我先存下来,等到后面再用。实现方法是用一个q_seg队列。
2:当且仅当当前节点时q_col队列为空才可能更新到答案,因为此时,上一个点一定是恰好K中颜色,当前点也是。
3:如果一个线段在左端点时它多余,再右端点时海没有颜色,说明这个线段是完全多余的,随便赋一个小于等于k的颜色即可。
4:q_seg的用途,是在每次遇到端点的时候判断是否要用的。同时每次判断不会出错。
证明:
①:如果这个线段无用,一定会在碰到它右端点的时候给它赋值,这里我们会特判这个线段是否有颜色。
②:如果它有用,它一定会在遇到右端点之前备用上,也就是赋值上一种颜色。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<ctime>
#define up(i,x,y) for(int i = x;i <= y;i ++)
#define down(i,x,y) for(int i = x;i >= y;i --)
#define mem(a,b) memset((a),(b),sizeof(a))
#define mod(x) ((x)%MOD)
#define lson p<<1
#define rson p<<1|1
using namespace std;
typedef long long ll;
const int SIZE = 400010;
const int INF = 2147483640;
const double eps = 1e-8;

inline void RD(int &x)
{
    x = 0;  char c; c = getchar();
    bool flag = 0;
    if(c == '-')    flag = 1;
    while(c < '0' || c > '9')   {if(c == '-')   {flag = 1;} c = getchar();}
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0',c = getchar();
}

struct Line{
	int pos,id;
//	Line(int a,int b):pos(a),id(b){}
}seg[SIZE];
int t,n,k,l,r,ans;
int col[SIZE];
queue <int> q_col,q_seg;

bool cmp(Line &a,Line &b){return a.pos == b.pos ? a.id < b.id : a.pos < b.pos;}

int main(int argc, char const *argv[])
{
	scanf("%d",&t);
	while(t--)
	{
		ans = 0;
		scanf("%d%d",&n,&k);
		while(!q_col.empty())	q_col.pop();
		while(!q_seg.empty())	q_seg.pop();
		for(int i = 1;i <= n;i ++)
		{
			scanf("%d%d",&l,&r);
			seg[i].pos = l;
			seg[n+i].pos = r;
			seg[i].id = i;
			seg[n+i].id = -i;
			col[i] = 0;
		}
		for(int i = 1;i <= k;i ++)	q_col.push(i);
		sort(seg+1,seg+1+2*n,cmp);
		for(int i = 1;i <= 2*n;i ++)
		{
		//	printf("ans:%d\n",ans);
			if(q_col.empty())
			{
		//		printf("seg:%d %d\n",seg[i].pos,seg[i-1].pos);
				ans += seg[i].pos - seg[i-1].pos;
			}
			if(seg[i].id > 0)//左端点
			{
				if(!q_col.empty())//填色
				{
					int now_color = q_col.front();
					q_col.pop();
					col[seg[i].id] = now_color;
				}
				else
				{
					q_seg.push(seg[i].id);
				}
			}
			else//右端点
			{
				if(col[- seg[i].id])	q_col.push(col[- seg[i].id]);
				else	col[- seg[i].id] = 1;
			}
			while(!q_seg.empty() && !q_col.empty())
			{
				int tmp_seg_id = q_seg.front();
				int tmp_col = q_col.front();
				q_seg.pop();
				if(col[tmp_seg_id])	continue;
				col[tmp_seg_id] = tmp_col;
				q_col.pop();
			}
		}
		printf("%d\n",ans);
		for(int i = 1;i <= n;i ++)
		{
			printf("%d%c",col[i],i == n ? '\n' : ' ');
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值