一直想看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还是没看完,先总结这么多吧