bzoj1006 [HNOI2008]神奇的国度

时间:2021-06-21 15:30:49

1006: [HNOI2008]神奇的国度

Time Limit: 20 Sec  Memory Limit: 162 MB
Submit: 2304  Solved: 1043

Description

K国是一个热衷三角形的国度,连人的交往也只喜欢三角原则.他们认为三角关系:即AB相互认识,BC相互认识,CA相互认识,是简洁高效的.为了巩固三角关系,K国禁止四边关系,五边关系等等的存在.所谓N边关系,是指N个人 A1A2...An之间仅存在N对认识关系:(A1A2)(A2A3)...(AnA1),而没有其它认识关系.比如四边关系指ABCD四个人 AB,BC,CD,DA相互认识,而AC,BD不认识.全民比赛时,为了防止做弊,规定任意一对相互认识的人不得在一队,国王相知道,最少可以分多少支队。

Input

第一行两个整数N,M。1<=N<=10000,1<=M<=1000000.表示有N个人,M对认识关系. 接下来M行每行输入一对朋友

Output

输出一个整数,最少可以分多少队

Sample Input

4 5
1 2
1 4
2 4
2 3
3 4

Sample Output

3

HINT

一种方案(1,3)(2)(4)

Source

题目大意:给出一个图,此图中不存在点数>3的环(这里的环指内部没边相连的环),问给此图的点染色,使得相邻的点颜色不同,所需要的最少颜色种类

分析:

首先此题有论文:

弦图与区间图-陈丹琦

http://wenku.baidu.com/link?url=Vz-Cjrq24tvblbk0l1I4IPYYfZ82jkGpzGBoNBDhi6qSCJko9hSTcrKGWrunO91TlU_eei4KBIdw1Y9U9bTnZOUJCPau8qTmUzcIzik_nQ3

简述一下:(省略一大堆证明之类的东东)

首先,像题目中那样的图(不存在点数>3的环)就做弦图,

一些点v1, v2, v3, ....., vn以及这些点之间相连的边叫这些点的诱导子图,

一些点的诱导子图如果是完全图,那就叫做团,

一个图中与一个点 i 相连的点为N( i ),

一个图中,如果有一个点 i 与N( i )的诱导子图是完全图的话,那么这个点 i 叫做单纯点

弦图中有样东西叫完美消除数列,这个数列是这样的:数列中第 i 个点在数列第 i - n 个点的诱导子图中是单纯点

怎么求完美消除序列:

我们定义Label[i]表示第 i 个点与多少已标记的点相连,一开始所有的点都没有标记,

然后选择一个Label最大的点 i ,标记这个点,并更新其他点的label,

按照标记顺序得到的数列便叫完美消除序列

这个可以用堆(优先队列)做,复杂度 nlogn,

也可以优化至O(n+m),建n-1个链表List,分别表示Label为x的有哪些(存入List[x]),

High记录最大的Label,

选择时:每次选择Label最大的点,从list[high]中从前往后选(注意忽略已经在完美消除序列中的点),选择到后,在list中删除此点,并加入完美消除序列

更新时:每次更新Label,比如从x改为x+1,那么不需要删除List[x]中的那个点,在List[x+1]的后面加入即可

复杂度O(n+m)

当然,本程序使用了优先队列

根据这个完美消除序列染色,每次染色都尽量染编号小(就是说尽量染以前的颜色,并保证不与 相邻的 已经染色的 点的颜色相同)的颜色,得到的颜色数就是答案,即最小染色数

综上所述,本题得解:

 #include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <ctime>
using namespace std;
typedef long long LL;
#define For(i, s, t) for(int i = (s); i <= (t); i++)
#define Ford(i, s, t) for(int i = (s); i >= (t); i--)
#define MIT (2147483647)
#define INF (1000000001)
#define MLL (1000000000000000001LL)
#define sz(x) ((bnt) (x).size())
#define clr(x, y) memset(x, y, sizeof(x))
#define puf push_front
#define pub push_back
#define pof pop_front
#define pob pop_back
#define ft first
#define sd second
#define mk make_pair
inline void SetIO(string Name) {
string Input = Name+".in",
Output = Name+".out";
freopen(Input.c_str(), "r", stdin),
freopen(Output.c_str(), "w", stdout);
} const int N = , M = ;
typedef pair<int, int> II;
int First[N], To[M*], Next[M*], Tot;
int n, m;
int Label[N], High, Que[N];
priority_queue<II> S;
bool Visit[N];
int Color[N], Draw[N]; inline void Insert(int u, int v) {
Tot++;
To[Tot] = v, Next[Tot] = First[u];
First[u] = Tot;
} inline void Input() {
scanf("%d%d", &n, &m);
For(i, , m) {
int u, v;
scanf("%d%d", &u, &v);
Insert(u, v), Insert(v, u);
}
} inline void Solve() {
High = , clr(Label, ), clr(Visit, );
For(i, , n) S.push(II(, i));
For(t, , n) {
II It = S.top();
while(Visit[It.sd]) {
S.pop();
It = S.top();
}
Visit[It.sd] = , Que[t] = It.sd;
for(int v, Tab = First[It.sd]; Tab; Tab = Next[Tab])
if(!Visit[v = To[Tab]]) {
Label[v]++;
S.push(II(Label[v], v));
}
} int Ans = ;
For(i, , n) Color[i] = n+;
clr(Draw, );
For(t, , n) {
int x = Que[t];
for(int Tab = First[x]; Tab; Tab = Next[Tab])
Draw[Color[To[Tab]]] = t;
Color[x] = ;
while(Draw[Color[x]] == t) Color[x]++;
Ans = max(Ans, Color[x]);
} printf("%d\n", Ans);
} int main() {
SetIO("");
Input();
Solve();
return ;
}

有些题外话也讲讲,都是我看论文懂得:

求弦图的最大独立集:最大独立集即为选择最多的点,使它们两两没边相连

根据完美消除序列选择点,每次如果当前点与以前选的点没边相连就选,不能选略过,

最终得到的点即为最大独立集

最大独立集为最少团覆盖(即用最少的团覆盖所有点):

是哪些团呢?-> 设最大独立集的点为 p1, p2, p3, ... pn, 那这些团为 p1+N(p1), p2+N(p2), .... pn+N(pn),如果与前面的团有重复的点,可以忽略那些点

判定弦图:zju1015

先按照求完美消除序列的方法求出来一个数列,

如果求出来的数列是完美消除序列,那么这个图就是弦图

判定完美消除序列:

我们首先可以暴力判定这个数列 pi, pi+1, pi+2.....pn是否两两相连,构成一个完全图,

复杂度很高

但也可以根据弦图的性质优化一下,

假设在pi,pi+1,....pn中与pi相连的点为 v1, v2, v3.... vn,

那么只需判断v1与 v2,v3,v4...vn是否相连即可

复杂度O(n+m)

还有区间图,区间图都可以转化为弦图,

即相互覆盖的两个线段连条边,

所以求选择最多线段不互相覆盖的问题可以转化为弦图的最大独立集来做来做,

等等等等,陈丹琦实在太厉害了,我是在太垃圾了。。。。。

我真是蒟蒻