[Noi2008]假面舞会

一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。 今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择 一个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具 的人。 为了使舞会更有神秘感,主办方把面具分为 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。

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<stdlib.h>
using namespace std;
vector<int> G[100005], GF[100005];
queue<int> q;
int bet, vis[100005], len[100005], in[100005], dp[100005], ans;
int Gcd(int x, int y)
{
	if(x==0)  return y;
	if(y==0)  return x;
	return Gcd(y, x%y);
}
void Sech(int u, int val)
{
	int i, v;
	len[u] = val;
	vis[u] = 1;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(vis[v]==0)
			Sech(v, val+1);
		else
			ans = Gcd(ans, abs(len[u]-len[v]+1));
	}
	for(i=0;i<GF[u].size();i++)
	{
		v = GF[u][i];
		if(vis[v]==0)
			Sech(v, val-1);
		else
			ans = Gcd(ans, abs(len[u]-len[v]-1));
	}
}
void Sech2(int u)
{
	int i, v;
	vis[u] = 1;
	bet = max(bet, dp[u]);
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(vis[v]==0)
			Sech2(v);
	}
	for(i=0;i<GF[u].size();i++)
	{
		v = GF[u][i];
		if(vis[v]==0)
			Sech2(v);
	}
}
int main(void)
{
	int i, j, x, y, n, m;
	//freopen("in.txt", "r", stdin);
	scanf("%d%d", &n, &m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d", &x, &y);
		G[x].push_back(y);
		GF[y].push_back(x);
		in[y]++;
	}
	for(i=1;i<=n;i++)
	{
		if(vis[i]==0)
			Sech(i, 0);
	}
	if(ans)
	{
		if(ans<3)
			printf("-1 -1\n");
		else
		{
			for(i=3;i<=ans;i++)
			{
				if(ans%i==0)
				{
					printf("%d %d\n", ans, i);
					break;
				}
			}
		}
	}
	else
	{
		memset(vis, 0, sizeof(vis));
		for(i=1;i<=n;i++)
		{
			if(in[i]==0)
			{
				dp[i] = 1;
				q.push(i);
				vis[i] = 1;
			}
		}
		while(q.empty()==0)
		{
			x = q.front();
			q.pop();
			vis[x] = 0;
			for(j=0;j<G[x].size();j++)
			{
				y = G[x][j];
				if(dp[x]+1>dp[y])
				{
					dp[y] = dp[x]+1;
					if(vis[y]==0)
					{
						vis[y] = 1;
						q.push(y);
					}
				}
			}
			for(j=0;j<GF[x].size();j++)
			{
				y = GF[x][j];
				if(dp[x]-1>dp[y])
				{
					dp[y] = dp[x]-1;
					if(vis[y]==0)
					{
						vis[y] = 1;
						q.push(y);
					}
				}
			}
		}
		for(i=1;i<=n;i++)
		{
			bet = 0;
			if(vis[i]==0)
				Sech2(i);
			ans += bet;
		}
		if(ans>=3)
			printf("%d 3\n", ans);
		else
			printf("-1 -1\n");
	}
	return 0;
}

​

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值