NOI 2008 假面舞会

如果没有环那么最大的面具的种类为所有联通块最长链之和、最小为3,如果最长链之和<3,则无解;

如果有环找出每个环的结点个数=、=面具种类数为所有环的结点个数的最大公约数x。

为什么呢

一个环1->2,2->3,3->4,4->5,5->6,6->1;那么可能的面具数为6或3种,是6的约数。

如果公约数小于3无解,大于3有解,最小的面具种数一定是>=3,<=x的一个x的约数。

那么最长链怎么求呢?
3->2->1->4->5;

如果枚举的话第一个是1,要求到最长链要枚举到3。。。。我们可以建立边的时候1->4这条边分为1->4为权值为1的边,4->1为权值为-1的边。那么枚举到1时,从1开始dfs,求每个点的dis,dis[3] = -2,dis[5] = 2;那么最长链的大小为dis[5] - dis[3] + 1;即遍历一次,最大的dis - 最小的dis +1;

那么环的个数怎么求呢?

tarjian?不行啊比如1->2->3.然而还有1->3这条边,然而这是个环,按以上建图法,tarjian回把它算为一个大小为3的环。。。然而这个环是不合法的。。。。所以我们采用dfs,如果没点v遍历过更新dis,如果遍历过环的大小为abs(dis[x] + tow - dis[v])(v是从x变了过来的,x->v这条边的边权为tow; )

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n, m;
int tov[2000005],h[100005],tp, nex[2000005], tow[2000005];
int vis[100005], dis[100005], ma, vis1[100005], maa, mii,dis1[100005];
void read(int &x)
{
	x = 0;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		c = getchar();
	}
	while( c >= '0' && c <= '9')
	{
		x = 10 * x + c -'0';
		c = getchar();
	}
}
int gcd(int n, int m)
{
	if(m == 0) return n;
	return gcd(m, n%m);
}
void add(int x,int y,int w)
{
	tp ++;
	nex[tp] = h[x];
	tow[tp] = w;
	h[x] = tp;
	tov[tp] = y;
}
void dfs(int x, int fa)
{
	for(int i = h[x]; i; i = nex[i])
	{
		int v = tov[i];
		if(v == fa) continue;
		if(vis[v] == 1)
		{
			int ha = dis[v] - (dis[x] + tow[i]);
			if(ha < 0) ha = (-1) * ha;
			if(ma == 0) ma = ha;
			else
			{
				if(ha != 0)
				{
					ma = gcd(ma,ha);
				}
			}
		}
		else 
		{
			vis[v] = 1;
			dis[v] = dis[x] + tow[i];
			dfs(v,x);
		}
	}
}
void dfs1(int x, int fa)
{
	for(int i = h[x]; i; i = nex[i])
	{
		int v = tov[i];
		if(vis1[v] == 1 || v == fa) continue;
		maa = max(maa , dis1[x] + tow[i]);
		mii = min(mii , dis1[x] + tow[i]);
		dis1[v] = dis1[x] + tow[i];
		vis1[v] = 1;
		dfs1(v,x);
	}
}
int main()
{
	read(n);
	read(m);
	for(int i = 1; i <= m; i++)
	{
		int x,y;
		read(x);
		read(y);
		add(x,y,1);
		add(y,x,-1);
	}
	for(int i = 1; i <= n; i++)
	{
		if(vis[i] == 0)
	    {
	     vis[i] = 1;
	     dfs(i,i);
		 } 
	}
	if(ma == 0)
	{
		int zm = 0;
		for(int i = 1; i <= n; i++)
		{
			if(vis1[i] == 0)
			{
				maa = 0, mii = 0; 
				vis1[i] = 1;
				dfs1(i,i);
				zm = zm + (maa - mii + 1);
			}
		}
		if(zm < 3)
		{
			printf("-1 -1");
			return 0;
		}
		 printf("%d 3",zm);
	}
	else
	{
		if(ma < 3) 
		{
			printf("-1 -1");
			return 0;
		}
		int mi = 0;
		for(int i = 3; i <= ma; i++)
		{
			if(ma % i == 0) 
			{
				 mi = i;
				 break;
			}
		}
	 printf("%d %d",ma,mi);
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Bluelanzhan/article/details/83307398