1. 程式人生 > >NOI 2008 假面舞會

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;
}