1006: [HNOI2008]神奇的国度 MCS

题目大意:给一些乱七八糟的关系,求涂色方案。

昨天手贱点开了,然后就入了完美消除序列的坑。

首先得有先基础知识 戳我 (作为一名啥专业术语都不懂的蒟蒻,看的我想吃屎啊)

根据完美消除序列的定义,如果我们求出完美消除序列,就可以贪心了。

贪心太水懒得写。

下面这一段话是上网找MCS时找到的,忘记在哪找到的了,反正是引用别人的,特别有助于理解模板。

蒟蒻来秀智商:
维护每个结点的“势”。并维护n个链表,分别表示势为0~n-1的结点各有哪些。
维护一个best值表示当前最大的势。
再开一个bool数组记录每个点是否已被取出放入序列。


初始时每个点势为0,都放在0号链表里。best=0。
①更新操作:某结点u的势从x变为x+1时,只要把u插到x+1链表的头部,无需将其从x链表中删除(所以不用写双向链表)。然后更新best值。
②取最大操作:取最大势结点时从best链表的头开始取,如果取到的是已被放入序列的结点就删除并继续往后取,仍取不到则--best,直到取到一个未被放入序列的点。
操作①每次O(1),总共执行O(m)次。best++也被执行O(m)次,那么操作②中--best也只有O(m)次。操作①每次插一个结点,则总共有O(n+m)个结点,而操作②中每往后取一个就删一个,所以也是O(n+m)。


然后就搞到O(n+m)了。。




要是dijkstra也这么搞的话复杂度就和边权有关了。。吃不消


然后就是代码了:

#include<cstdio>
#include<cstdlib>
#include<cstring>
struct node{
	int x,y,next;
}sa[3900000];
int first[10010],last[10010],len=0;
int n,m;
void ins(int h[],int x,int y)
{
	len++;
	sa[len].x=x;
	sa[len].y=y;
	sa[len].next=h[x];
	h[x]=len;
}
int best=0,seq[10110],f[10110];
bool tf[10100];
int mymax(int x,int y)
{
	if(x>y) return x;
	return y;
}
void MCS()
{
	for(int i=1;i<=n;i++)
	ins(last,0,i);
	for(int j=n;j>=1;j--)
	{
		while(1)
		{
			int i;
			for(i=last[best];i!=-1;i=sa[i].next)
			{
			if(tf[sa[i].y]==false) break;
			else last[best]=sa[i].next;
			}
			
			if(i!=-1)
			{
				int x=sa[i].y;
				tf[x]=true;seq[j]=x;
				for(int i=first[x];i!=-1;i=sa[i].next)
				{
					if(tf[sa[i].y]==false)
					{
					f[sa[i].y]++;
					ins(last,f[sa[i].y],sa[i].y);
					best=mymax(best,f[sa[i].y]);
					}
					
				}
				break;
			}
			else best--;
		}
	}
}
int mark[100010],color[100010];
int main()
{
	scanf("%d%d",&n,&m);
	memset(first,-1,sizeof(first));
	memset(last,-1,sizeof(last));
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ins(first,x,y);
		ins(first,y,x);
	}
	memset(f,0,sizeof(f));
	memset(tf,false,sizeof(tf));
	MCS();
//	for(int j=n;j>=1;j--)
//	printf("%d\n",seq[j]);
	//贪心
	int ans=0,i,j;
    for(j=n;j;j--)
	{
		int x=seq[j];
		for(i=first[x];i!=-1;i=sa[i].next)
		{
			mark[color[sa[i].y]]=j;
			//printf("%d\n",sa[i].y);
		}
		//printf("\n");
		for(i=1;i<=n&&mark[i]==j;i++);//printf("!");
		color[x]=i;
		ans=mymax(ans,i);
	} 
	printf("%d\n",ans);
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值