bzoj-1006 神奇的国度

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出一个弦图,求它的最小染色数;

n<=10000,m<=1000000;


题解:

膜CDQ。。。

似乎很多NP问题在弦图这样的特殊图中都有优雅的解法;

并且对于弦图,它的完美消除序列可以搞很多事情。。

这里只介绍一下各种结论和实现的细节吧,具体推荐去看2009WC的CDQ的PPT,以及这篇PDFhttp://www.docin.com/p-610139511.html

我看完了LexBFS和最大势算法的过程,感觉上其实二者比较相似;

但是显然MCS只需要维护一个数字比较优越,于是就选择了MCS啦;


实现中就是用链表维护[0,n-1]个桶,每次取出最大桶中的任意一个元素,从大到小标号,将与其连边的点的势+1;

然后删一删加一加就可以了,分析一下时间复杂度;

因为是用双向链表实现,所以存取元素过程都是O(1)的;

维护非空最大桶的编号,由于所有点的势总共加了m次,那么编号最多加了m次(并且不可能大于n-1),也就最多减了m次;

从桶中取元素并标号进行了n次,对当前元素周围的点增加势进行了m次,因此时间复杂度就是O(n+m)了;

我们这样就得到了一个标号序列,并且如果G为弦图,那么这个序列为它的完美消除序列(之一);


(为了愉悦)(为了水下一道题)为了验证数据的正确性,我顺便写了一下对完美消除序列的判定;

这个判定朴素的过程复杂度非常爆炸,在完全图上大概是O(n^3)吧,不过我们还是有证明!

我们可以顺着我们得到的序列,对于每一个序列中的点,我们找到序列中在它之后的所有与它相连的点;

只需要判定其中的第一个点是否与其他所有点相连即可,时间复杂度O(n+m);

然而这个n+m还要一些姿势。。我同样选择了链表;

对于每一个点,我们再维护一个叫test的链表,记录所有序列中在它前面的点让它检验的点;

维护的时候只要记录边表中序列排名最靠前的点,然后再扫一遍边表加到那个点的test里就可以了,这个过程只扫了两次边表,时间O(m),并且可见test链表的总空间也是O(m)的;

检验的时候将与这个点连边的所有序列中在它之后出现的点加到一个布尔数组里,然后扫一遍test表,看看是否都在布尔数组中,不在则跳出返回false,都在就再扫一遍边表清掉布尔数组;

因此,这个算法的时间和空间都是O(n+m)的;


现在万事俱备,只差求出弦图的最小染色了!

这次和判定完美消除序列很像,只不过正着扫变成了倒着扫;

然后对于每个点,它只要考虑序列中在它后面且与它连边的点的颜色,然后取一个除那以外最小的;

实现和上一个很像,也是维护链表,然后扔到记录颜色的布尔数组里,之后暴力扫最小的;

时间复杂度也是O(n+m),至此问题被完美解决;


链表真是太神奇啦!!!从最开始的邻接表到各种各样的神奇优化,图论的各种线性算法都有这东西出现啊;

由此可见,线性复杂度数据结构才是真正的大杀器啊。。。


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 11000
#define M 1100000
using namespace std;
struct node
{
	node *l,*r;
	bool vis;
	int c,no;
}*B[N],a[N];
int next[M<<1],to[M<<1],head[N],ce;
int v[N],rank[N],col[N],best;
bool vis[N];
int test[N],val[M],_next[M],_ce;
void add(int x,int y)
{
	to[++ce]=y;
	next[ce]=head[x];
	head[x]=ce;
}
void _add(int x,int y)
{
	val[++_ce]=y;
	_next[_ce]=test[x];
	test[x]=_ce;
}
int main()
{
	int n,m,i,j,k,x,y,ans;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	for(i=1;i<=n;i++)
	{
		a[i].l=&a[i-1];
		a[i].r=&a[i+1];
		a[i].no=i;
	}
	a[1].l=a[n].r=NULL;
	B[0]=&a[1];
	for(i=n;i>=1;i--)
	{
		while(B[best]==NULL)
			best--;
		v[i]=B[best]->no;
		rank[v[i]]=i;
		if(B[best]->r!=NULL)
			B[best]->r->l=NULL;
		B[best]->vis=1;
		B[best]=B[best]->r;
		for(j=head[v[i]];j;j=next[j])
		{
			y=to[j];
			if(a[y].vis)	continue;
			a[y].c++;
			if(a[y].l!=NULL)
				a[y].l->r=a[y].r;
			else
				B[a[y].c-1]=a[y].r;
			if(a[y].r!=NULL)
				a[y].r->l=a[y].l;
			a[y].r=B[a[y].c];
			a[y].l=NULL;
			if(B[a[y].c]!=NULL)
				B[a[y].c]->l=&a[y];
			B[a[y].c]=&a[y];
			best=max(best,a[y].c);
		}
	}
	for(i=1;i<=n;i++)
	{
		x=v[i];
		for(j=head[x],y=0;j;j=next[j])
		{
			if(rank[to[j]]>i)
			{
				vis[to[j]]=1;
				if(!y||rank[y]>rank[to[j]])
					y=to[j];
			}
		}
		for(j=test[x];j;j=_next[j]) 
		{
			if(!vis[val[j]])
			{
				while(1)	puts("fuck♂Q!");
			}
		}
		for(j=head[x];j;j=next[j])
		{
			if(rank[to[j]]>i)
			{
				vis[to[j]]=0;
				if(to[j]!=y)
					_add(y,to[j]);
			}
		}
	}
	memset(test,0,sizeof(test));
	_ce=0;
	for(i=n,ans=0;i>=1;i--)
	{
		x=v[i];
		for(j=test[x];j;j=_next[j]) 
		{
			vis[val[j]]=1;
		}
		for(j=1;;j++)
		{
			if(!vis[j])
			{
				col[x]=j;
				ans=max(ans,j);
				break;
			}
		}
		for(j=test[x];j;j=_next[j]) 
		{
			vis[val[j]]=0;
		}
		for(j=head[x],y=0;j;j=next[j])
		{
			if(rank[to[j]]<i)
			{
				_add(to[j],col[x]);
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值