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
1 2
1 4
2 4
2 3
3 4
Sample Output
3
HINT
一种方案(1,3)(2)(4)
---------------------------------------------------------------------------------------------------------------------------------------------
被cena略微坑了一下,第一次交部分暴力, cena上面测的最大一组0.89s, O(m+n)优化后再测, 居然是1.5s+......
---------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------
被cena略微坑了一下,第一次交部分暴力, cena上面测的最大一组0.89s, O(m+n)优化后再测, 居然是1.5s+......
然后作死地在bzoj上一交, 结果如下:
比第一次快了不少, 可能是windows或者cena的问题, 不过我也不准备深究了。
---------------------------------------------------------------------------------------------------------------------------------------------
了解题意后, 发现这是一道最小点染色的问题, 不过用普通方法做是一定会超时的
首先我们要了解一个叫弦图的定义: 当图中任意长度大于3的环都至少有一个弦时, 一个无向图称为弦图 。
比第一次快了不少, 可能是windows或者cena的问题, 不过我也不准备深究了。
---------------------------------------------------------------------------------------------------------------------------------------------
了解题意后, 发现这是一道最小点染色的问题, 不过用普通方法做是一定会超时的
首先我们要了解一个叫弦图的定义: 当图中任意长度大于3的环都至少有一个弦时, 一个无向图称为弦图 。
对于这道题, 题目中提到的不存在四角、五角等关系就说明这个图是一个弦图。 我们可以用弦图的特殊性质去做, 当然, 这又涉及到许许多多的定义了。 (
点击打开链接如果读了这篇文章可直接跳至下个分界线处)
一大波定义即将到来......
子图: 图G = (V, E), 则G' = (V', E'), V' ⊆V, E' ⊆ E 为图G的子图。
一大波定义即将到来......
子图: 图G = (V, E), 则G' = (V', E'), V' ⊆V, E' ⊆ E 为图G的子图。
团:
图G的一个子图G' = (V', E'
)
,G'
为关于V',
的完全图。
诱导子图:G = (V, E), G' = (V', E'), V' ⊆V, E' = {(u, v)|u,v∈V', (u, v) ∈E } 中, 图G'称为图G的诱导子图。
诱导子图:G = (V, E), G' = (V', E'), V' ⊆V, E' = {(u, v)|u,v∈V', (u, v) ∈E } 中, 图G'称为图G的诱导子图。
单纯点:设N(v)表示与点v相邻的点集。一个点称为单纯点当{v} + N(v)的诱导子图为一个团。
完美消除序列: 一个点的序列(每个点出现且恰好出现一次)v1, v2, …, vn满足vi在{vi, vi+1,…,vn}的诱导子图中为一个单纯点。
完美消除序列: 一个点的序列(每个点出现且恰好出现一次)v1, v2, …, vn满足vi在{vi, vi+1,…,vn}的诱导子图中为一个单纯点。
那么, 完美消除序列从后往前依次给每个点染色,给每个点染上可以染的最小的颜色。这时染的最大的颜色即为答案。
求完美消除序列时, 用到了最大势算法(其实是因为LexBFS不会写QAQ), 这样一来, 整道题的框架就出来了。不过最大势算法的复杂度却是关键, 前面已经说过, 第一次提交时我用的是O(n²) 的暴力算法, 但显然有更优的, 用堆可以优化到O(nlogn), 但我更倾向于O(n+m)的链表求法。
考虑从0到n的链表, 数字表示该链表中的点的当前势, 同时用一个数组flag和一个变量maxs记录点是否在序列中和当前最大势。 每次在最大势的链表上取出一个不再序列中的点, 若链表取完, 则maxs--, 直到取出一个点。 将该点加到序列中, 并将与之相连的不再序列中的点的势+1, 更新maxs并将之挂到相应的链表上, 这样就做到了O(n+m)的复杂度。
---------------------------------------------------------------------------------------------------------------------------------------------
CODE:
#include <cstdio>
#include <cstring>
#include <queue>
#include <cstdlib>
#define N 10000 + 10
#define M 2000000 + 10
using namespace std;
struct edge
{
int to, next;
}e[M];
int n, m, num, ans, maxs;
int p[N], seq[N], col[N], lab[N], flag[N];
struct node
{
int now;
node *next;
}f[N];//链表
void add(int x, int y)
{
e[++num].to = y;
e[num].next = p[x];
p[x] = num;
}
void put(int x)
{
node *po = (struct node *)malloc(sizeof(struct node));
po->next = f[lab[x]].next;
po->now = x;
f[lab[x]].next = po;
}//链表的插入
void read(int &x)
{
x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + c - '0';
c = getchar();
}
}
void init()
{
int x, y;
read(n), read(m);
for (int i = 1; i <= m; ++i)
{
read(x), read(y);
if (x == y) continue;
add(x, y);
add(y, x);
}
}
void create()
{
for (int i = 1; i <= n; ++i)
f[i].next = NULL;//
for (int i = 1; i <= n; ++i) put(i);
maxs = 0;//初始化
for (int i = n; i; i--)//用逆序求
{
node *po = f[maxs].next;//找到当前最大
while(flag[po->now])
{
f[maxs].next = po = po->next;//及时删除没有用的点(漏掉的话会超时)
while(po == NULL)
{
maxs--;
po = f[maxs].next;
}
}
f[maxs].next = po->next;//更新
while(f[maxs].next == NULL) maxs--;
int x = po->now;
flag[x] = 1, seq[i] = x;
for (int j = p[x]; j; j = e[j].next)
if (!flag[e[j].to])
{
++lab[e[j].to];//加势
if (lab[e[j].to] > maxs) maxs = lab[e[j].to];
put(e[j].to);
}
}
}
void paint()
{
for (int i = 1; i <= n; ++i)
flag[i] = -1;
for (int i = n; i; i--)
{
int x = seq[i];
for (int j = p[x]; j; j = e[j].next)
flag[col[e[j].to]] = i;
for (int j = 1; j <= n; ++j)
if (flag[j] != i)
{
col[x] = j;
break;
}
if (ans < col[x]) ans = col[x];
}
}
void deal()
{
create();
paint();
printf("%d\n", ans);
}
int main()
{
freopen("kingdom.in", "r", stdin);
freopen("kingdom.out", "w", stdout);
init();
deal();
return 0;
}