NOI 2008 假面舞会 party

NOI Add comments1,157 views

这道题难在对图的深入分析。如果一个人能够看见两个不同的人,那么这两个人所戴的面具种类一定相同,同样,如果一个人能被两个人看见,这两个人所戴 的面具种类也一定相同。把每个人当作图中的一个顶点,a能看见b,则连接(a,b)一条有向边。我们把能够推断出的带相同面具的人成为一个类。

显然图中可能会有多个连通分量,对于某个连通分量,如果把边的方向去掉,该连通分量中存在一个无向环,则称该连通分量为环结构,否则成为 树结构。可以发现,一个环结构的连通分量,逐步把一个类的所有顶点收缩为一个顶点,最终一定会剩下一个有向环,而一个树结构的连通分量也会变成一个单向的 有向树。一个环结构的连通分量,最终有向环上顶点个数,就是该连通分量的最大类数,而且,最大类数的所有约数也都可以满足该连通分量的类数。而树结构连通 分量,有向树中的最长路径,就是该连通分量的最大类数。

显然环结构是关键的,如果同时存在树结构和环结构的连通分量,树结构可以忽略。如果存在两个(或多个)环结构连通分量,它们类数的最大公约数可以就是满足这两个连通分量的最大类数,它们类数的最大公约数的大于等于3的最小公约数就是满足这两个连通分量的最小类数。

关键问题就是求一个环结构连通分量的等价环的长度。收缩等位顶点的方法不便于编写,我们可以用一遍DFS的方法。对于一个连通分量,从一个 顶点开始DFS,有向边可以双向走,第一个顶点标号1。遇到正向的边,标号加1,遇到反向的边,标号减1。遇到已经访问过的点,则说明找到了一个等价环, 长度为该点应该被标的标号减去已有的标号的差的绝对值,标号不改变然后返回继续DFS。这样以O(N+M)的时间求出所有等价环长度,算出最大和最小公约 数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* 
 * Problem: NOI2008 party
 * Author: Guo Jiabao
 * Time: 2009.3.4 22:41
 * State: Solved
 * Memo: dfs找环和链 求公约数
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
const int MAXN=100001,MAXM=1000001;
using namespace std;
struct edge
{
	edge *next,*op;
	int t,c;
	bool nuked;
}ES[MAXM];
struct vertex
{
	edge *f,*l;
	int it,bl;
	bool vis;
}V[MAXN];
int N,M,EC,Ccnt,Gans,Mans,D;
int C[MAXN],BMax[MAXN],BMin[MAXN];
inline edge * addedge(int a,int b,int c)
{
	if (V[a].l)
		V[a].l=V[a].l->next=&ES[++EC];
	else
		V[a].f=V[a].l=&ES[++EC];
	ES[EC].t=b;
	ES[EC].c=c;
	ES[EC].nuked=false;
	return &ES[EC];
}
void init()
{
	int i,a,b;
	freopen("party2008.in","r",stdin);
	freopen("party2008.out","w",stdout);
	scanf("%d%d",&N,&M);
	for (i=1;i<=M;i++)
	{
		scanf("%d%d",&a,&b);
		edge *e1=addedge(a,b,1);
		edge *e2=addedge(b,a,-1);
		e1->op=e2;
		e2->op=e1;
	}
	Gans=0;
}
inline int ABS(int a) {return a<0?-a:a;}
void findcircle(int u,int it)
{
	V[u].it=it;
	V[u].vis=true;
	for (edge *k=V[u].f;k;k=k->next)
	{
		if (k->nuked) continue;
		k->nuked=k->op->nuked=true;
		int v=k->t;
		if (!V[v].vis)
			findcircle(v,it+k->c);
		else
		{
			if (it+k->c - V[v].it!=0)
				C[++Ccnt]=ABS(it+k->c - V[v].it);
		}
	}
}
inline int gcd(int a,int b)
{
	int c;
	while (b)
	{
		c=a%b;
		a=b;
		b=c;
	}
	return a;
}
void findpart(int u)
{
	V[u].bl=D;
	for (edge *k=V[u].f;k;k=k->next)
	{
		int v=k->t;
		if (V[v].bl==0)
			findpart(v);
	}
}
void part() //求连通分量
{
	int i;
	D=0;
	for (i=1;i<=N;i++)
		V[i].bl=0;
	for (i=1;i<=N;i++)
	{
		if (V[i].bl==0)
		{
			++D;
			findpart(i);
			BMin[D]=0x7FFFFFFF;
			BMax[D]=-0x7FFFFFFF;
		}
	}
}
void furthest(int u,int it)
{
	V[u].it=it;
	V[u].vis=true;
	for (edge *k=V[u].f;k;k=k->next)
	{
		if (k->nuked) continue;
		k->nuked=k->op->nuked=true;
		int v=k->t;
		furthest(v,it+k->c);
	}
}
void link()
{
	int i;
	for (i=1;i<=N;i++)
	{
		V[i].vis=false;
		for (edge *k=V[i].f;k;k=k->next)
			k->nuked=k->op->nuked=false;
	}
	for (i=1;i<=N;i++)
	{
		if (!V[i].vis)
			furthest(i,1);
		if (V[i].it>BMax[ V[i].bl ])
			BMax[ V[i].bl ]=V[i].it;
		if (V[i].it<BMin[ V[i].bl ])
			BMin[ V[i].bl ]=V[i].it;
	}
	Gans=0;
	for (i=1;i<=D;i++)
		Gans+=BMax[i]-BMin[i]+1;
	if (Gans < 3)
		Gans=Mans=-1;
	else
		Mans=3;
}
void solve()
{
	int i,GCD,LCD,MIN;
	for (i=1;i<=N;i++)
		if (!V[i].vis)
			findcircle(i,1);
	MIN=GCD=C[1];
	for (i=2;i<=Ccnt;i++)
	{
		GCD=gcd(GCD,C[i]);
		if (C[i] < MIN)
			MIN = C[i];
	}
	for (LCD=3;LCD<=MIN;LCD++)
	{
		for (i=1;i<=Ccnt;i++)
			if (C[i] % LCD !=0)
				break;
		if (i>Ccnt)
			break;
	}
	if (Ccnt==0) //无环
	{
		part();
		link();
	}
	else if (GCD>=3)
	{
		Gans=GCD;
		Mans=LCD;
	}
	else
		Gans=Mans=-1;
}
int main()
{
	init();
	solve();
	printf("%d %dn",Gans,Mans);
	return 0;
}

假面舞会

【问题描述】

一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。为了使舞会更有神秘感,主办方把面具分为k (k≥3)类,并使用特殊的技术将每个面具的编号标在了面具上,只有戴第i 类面具的人才能看到戴第i+1 类面具的人的编号,戴第k 类面具的人能看到戴第1 类面具的人的编号。 参加舞会的人并不知道有多少类面具,但是栋栋对此却特别好奇,他想自己算出有多少类面具,于是他开始在人群中收集信息。

栋栋收集的信息都是戴第几号面具的人看到了第几号面具的编号。如戴第2号面具的人看到了第5 号面具的编号。栋栋自己也会看到一些编号,他也会根据自己的面具编号把信息补充进去。由于并不是每个人都能记住自己所看到的全部编号,因此,栋栋收集的信 息不能保证其完整性。现在请你计算,按照栋栋目前得到的信息,至多和至少有多少类面具。由于主办方已经声明了k≥3,所以你必须将这条信息也考虑进去。

【输入格式】

输入文件party.in 第一行包含两个整数n, m,用一个空格分隔,n 表示主办方总共准备了多少个面具,m 表示栋栋收集了多少条信息。

接下来m 行,每行为两个用空格分开的整数a, b,表示戴第a 号面具的人看到了第b 号面具的编号。相同的数对a, b 在输入文件中可能出现多次。

【输出格式】

输出文件party.out 包含两个数,第一个数为最大可能的面具类数,第二个数为最小可能的面具类数。如果无法将所有的面具分为至少3 类,使得这些信息都满足,则认为栋栋收集的信息有错误,输出两个-1。

【输入样例一】

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

【输出样例一】

4 4

【输入样例二】

3 3
1 2
2 1
2 3

【输出样例二】

-1 -1

【数据规模和约定】

  • 50%的数据,满足n ≤ 300, m ≤ 1000;
  • 100%的数据,满足n ≤ 100000, m ≤ 1000000。

Maybe you like

标签:, , , ,


5 Responses to “NOI 2008 假面舞会 party”

  1. CmYkRgB123 » Blog Archive » NOI 2008 解题报告 Says:
    WordPress 2.7.1

    [...] « NOI 2008 假面舞会 party 三 [...]

  2. hzhua Says:
    Firefox 3.0.7Debian GNU/Linux

    1->2
    1->3
    2->4
    3->4
    这样的图,2,3是同类的。
    去掉方向有环1-2-4-3-1
    缩点后为1->(2,3)->4 并没有有向环阿

    是我理解错了么?

    [回复]

  3. wangsincos Says:
    Internet Explorer 6.0Windows XP

    如果没有环状结构,为什么最大可能的面具类数是有向树中的最长路径,而不是任意值?

    [回复]

    svrggfgdsf 回复:
    Firefox 3.5.9Windows 7

    应该不是任意值.如果没有环, 则应统计每个连通图分量中重要类号的面具个数总和s,再n-s求出.
    s= 已收集的面具总个数 – 各连通图的类数之和

    [回复]

  4. svrggfgdsf Says:
    Firefox 3.5.9Windows 7

    而且退一步讲即使没有环时,上面的结果也有可能是错误的. 原因在于V[i]中不存在值时D++依然会执行. 如果有t个空值,会造成D值比期望值大t吧..?怀疑中..

    [回复]

Leave a Reply

36 queries. 0.656 seconds. Designed by NattyWP .
Images by desEXign.