弦图和区间图

一直想看CDQ的课件,但是一直畏惧课件的长度没敢看,今天终于看完啦

还是很简单的嘛        八月份!三个月!吼吼!

 

做一些总结吧,以下是本人的理解:

1、设图的点集为V,边集为E

则其诱导子图的点集为V‘,边集为E’

满足V‘属于V,E’属于E,且E‘中任意边的两个端点均属于V’

2、弦是一个环中不在环上的且连接环上两点的边

弦图中任意长度>3的环一定存在至少一条弦

换句话说,弦图中最多只有三元环,其余的大环都是小环凑成的

3、团:若对于任意两点u,v属于V,都存在(u,v)属于E

则称这个图为一个团

4、单纯点:设点u,跟u相连的点为N(u)

若存在V‘=u U N(u),使得形成的原图的诱导子图是一个团

则称u是一个单纯点

5、完美消除序列:设V的一个排列{v1,……,vn}

使得任意一个下标i均满足vi在{vi,……vn}的诱导子图是一个单纯点的序列

我们称之为完美消除序列

6、如何判断一个图是否是弦图

定理:一个图是弦图当且仅当它有一个完美消除序列

证明大略:

充分性

先证明弦图至少有一个单纯点和弦图的诱导子图也是弦图

之后运用数学归纳法假设<n的弦图具有完美消除序列,则当前的弦图的完美消除序列可以通过一个单纯点和<n的弦图的完美消除序列得到

必要性

设一个图不是弦图,那这个图至少存在一个>3的小环

设v,v1,v2是环上三点,v和v1,v2相连,v1和v2不相连(长度>3)

设v在完美消除序列中第一次出现,由于v1和v2不相连,所以v不是{v,……,vn}的单纯点

对于充分性的证明可以启发我们采用MCS算法来求弦图的完美消除序列

MCS算法大意如下:

给每个点定义lebel值表示有多少个已选择的点和其相连

逆序构建完美消除序列,每次选择未选择的label值最高的点放入完美消除序列中

同时更新label值

如何判断一个完美消除序列是否合法?

我们考虑逆序扫完美消除序列,假设我们扫到vi

只需要判断vi在{vi……vn}是否是一个单纯点即可

暴力判断显然不行

设跟vi在{vi……vn}相连的点为vj1……vjk(按完美消除序列顺序)

那么不难发现我们已经检查过vj1的单纯点性质了

我们只需要考虑vj1是否和vj2……vjk相连即可

这样判断总时间复杂度O(N+M)

CDQ的课件中说MCS的时间复杂度为O(N+M),但是不清楚桶的做法

所以我最多只会O(NlogN+M)的做法(用堆)

下面是一道模板题:ZOJ  1015 O(n^2)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;

const int maxn=1010;
int n,m,u,v;
int h[maxn],cnt=0;
struct edge{
	int to,next;
}G[2000010];
int pos[maxn],check[maxn];
bool vis[maxn];
void add(int x,int y){
	++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;
}
void read(int &num){
	num=0;char ch=getchar();
	while(ch<'!')ch=getchar();
	while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void Clear(){
	memset(pos,0,sizeof(pos));
	memset(check,0,sizeof(check));
	memset(vis,false,sizeof(vis));
}
bool MCS(){
	for(int now=n;now>=1;--now){
		int mx=-1,mn=23333;
		int A=0,B=0;
		for(int i=1;i<=n;++i)if(!pos[i]&&check[i]>mx)mx=check[i],A=i;
		pos[A]=now;
		for(int i=h[A];i;i=G[i].next){
			int v=G[i].to;
			if(!pos[v])check[v]++;
			if(pos[v]>pos[A]&&pos[v]<mn)mn=pos[v],B=v;
		}
		for(int i=h[B];i;i=G[i].next){
			int v=G[i].to;
			vis[v]=true;
		}vis[B]=true;
		for(int i=h[A];i;i=G[i].next){
			int v=G[i].to;
			if(pos[v]>pos[A]&&!vis[v])return false;
		}
		for(int i=h[B];i;i=G[i].next){
			int v=G[i].to;
			vis[v]=false;
		}vis[B]=false;
	}return true;
}

int main(){
	while(scanf("%d%d",&n,&m)==2){
		if(!n&&!m)break;
		memset(h,0,sizeof(h));cnt=0;
		for(int i=1;i<=m;++i){
			read(u);read(v);
			add(u,v);add(v,u);
		}
		Clear();
		if(MCS())printf("Perfect\n\n");
		else printf("Imperfect\n\n");
	}return 0;
}

7、弦图染色问题

直接贪心就可以了,逆序扫完美消除序列

给每个点染能染的最小颜色,分组的答案是颜色的最大值

时间复杂度O(N+M)

HNOI 神奇的国度

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
 
const int maxn=10010;
int n,m,u,v;
int ans;
int h[maxn],cnt=0;
int pos[maxn],fp[maxn];
int check[maxn];
int vis[maxn],c[maxn];
struct edge{
    int to,next;
}G[2000010];
 
void read(int &num){
    num=0;char ch=getchar();
    while(ch<'!')ch=getchar();
    while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void add(int x,int y){
    ++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;
}
void MCS(){
    for(int now=n;now>=1;--now){
        int mx=-1,A=0;
        for(int i=1;i<=n;++i)if(!pos[i]&&check[i]>mx)mx=check[i],A=i;
        pos[A]=now;fp[now]=A;
        for(int i=h[A];i;i=G[i].next){
            int v=G[i].to;
            if(!pos[v])check[v]++;
        }
    }return;
}
void Get_ans(){
    for(int now=n;now>=1;--now){
        int u=fp[now];
        for(int i=h[u];i;i=G[i].next){
            int v=G[i].to;
            vis[c[v]]=now;
        }
        for(int i=1;i;++i){
            if(vis[i]!=now){c[u]=i;break;}
        }
    }
    for(int i=1;i<=n;++i)ans=max(ans,c[i]);
}
int main(){
    read(n);read(m);
    for(int i=1;i<=m;++i){
        read(u);read(v);
        add(u,v);add(v,u);
    }
    MCS();Get_ans();
    printf("%d\n",ans);
    return 0;
}

8、弦图的最大独立集问题和最小团覆盖

很容易知道最大独立集=最小团覆盖

最大独立集就是正序扫完美消除序列,能选就选

9、区间图

将有重叠部分的区间之间建边,形成区间图

可以证明区间图一定是弦图

所以我们可以用弦图的做法求区间的各种东西

另外值得一提的是区间图可以不用MCS求完美消除序列

将区间按右端点从小到大排序后的结果就是完美消除序列

cojs 简单的区间问题

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=100010;
int n;
struct Pos{
	int L,R;
}c[maxn];
int vis[maxn];
int col[maxn];

bool cmp(const Pos &A,const Pos &B){
	if(A.R==B.R)return A.L>B.L;
	return A.R<B.R;
}
void read(int &num){
	num=0;char ch=getchar();
	while(ch<'!')ch=getchar();
	while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void Solve_one(){
	int R=-1,ans=0;
	for(int i=1;i<=n;++i){
		if(c[i].L>R)ans++,R=c[i].R;
	}printf("%d\n",ans);
}
void Solve_two(){
	int ans=0;
	for(int i=n;i>=1;--i){
		for(int j=i+1;j<=n;++j){
			if(c[j].L<=c[i].R)vis[col[j]]=i;
		}
		for(int j=1;j;j++){
			if(vis[j]!=i){col[i]=j;break;}
		}ans=max(ans,col[i]);
	}printf("%d\n",ans);
}

int main(){
	freopen("get_pos.in","r",stdin);
	freopen("get_pos.out","w",stdout);
	read(n);
	for(int i=1;i<=n;++i)read(c[i].L),read(c[i].R);
	sort(c+1,c+n+1,cmp);
	Solve_one();Solve_two();
	return 0;
}

CDQ的PPT还是没看完,先总结这么多吧

 

posted @ 2016-05-10 10:57  _Vertical  阅读(502)  评论(0编辑  收藏  举报