[Luogu P1477] [BZOJ 1064] [NOI2008]假面舞会

洛谷传送门
BZOJ传送门

题目描述

一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。

今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。

为了使舞会更有神秘感,主办方把面具分为 k(k3) k ( k ≥ 3 ) 类,并使用特殊的技术将每个面具的编号标在了面具上,只有戴第 i i 类面具的人才能看到戴第i+1 类面具的人的编号,戴第 k k 类面具的人能看到戴第 1 类面具的人的编号。

参加舞会的人并不知道有多少类面具,但是栋栋对此却特别好奇,他想自己算出有多少类面具,于是他开始在人群中收集信息。

栋栋收集的信息都是戴第几号面具的人看到了第几号面具的编号。如戴第 2 2 号面具的人看到了第 5 号面具的编号。栋栋自己也会看到一些编号,他也会根据自己的面具编号把信息补充进去。

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

输入输出格式

输入格式:

第一行包含两个整数 n,m n , m ,用一个空格分隔, n n 表示主办方总共准备了多少个面具,m 表示栋栋收集了多少条信息。接下来 m m 行,每行为两个用空格分开的整数 a,b,表示戴第 a a 号面具的人看到了第 b 号面具的编号。相同的数对 a,b a , b 在输入文件中可能出现多次。

输出格式:

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

输入输出样例

输入样例#1:
6 5
1 2
2 3
3 4
4 1
3 5
输出样例#1:
4 4
输入样例#2:
3 3
1 2
2 1
2 3
输出样例#2:
-1 -1

说明

50%的数据,满足 n300,m1000 n ≤ 300 , m ≤ 1000

100%的数据,满足 n100000,m1000000 n ≤ 100000 , m ≤ 1000000

解题分析

因为一种面具的人只能看到另一种面具的人, 所以我们完全可以 DFS D F S 的时候讲同一深度的点染成同一颜色。

如果图中出现环, 那么最大的可能答案就是这些环的 gcd g c d , 最小值就是最大值的第一个超过 2 2 的因数。

如果图中只有链, 那么最大值就是点数, 最小值显然=3

但是直接 DFS D F S 会有一点问题: 可能存在多条链, 其末端为一个点, 这样我们染色的时候就会多染。

有一个很妙的解决方式:我们将正向边的边权设为 1 1 , 反向边的边权设为1, 就可以一遍 DFS D F S 得到一个联通快内的答案。 用 mn[S] m n [ S ] 表示涂色得到的最小值, mx[S] m x [ S ] 表示涂色得到的最大值, 那么 S S 内的最长链的长度为mx[S]mn[S]+1

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cmath>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MD 100050
#define ME 1000050
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
struct Edge {int to, nex, val;} edge[ME << 1];
int head[MD], col[MD], bel[MD], mx[MD], mn[MD];
bool vis[MD];
int dot, line, cnt, ans1, ans2, len, tar;
int find(R int now) {return bel[now] == now ? now : bel[now] = find(bel[now]);}
IN void add(R int from, R int to, R int val)
{edge[++cnt] = {to, head[from], val}, head[from] = cnt;}
int gcd(R int x, R int y) {return y ? gcd(y, x % y) : x;}
void DFS(R int now)
{
    mx[tar] = std::max(mx[tar], col[now]);
    mn[tar] = std::min(mn[tar], col[now]);
    for (R int i = head[now]; i; i = edge[i].nex)
    {
        if(col[edge[i].to]) ans1 = gcd(ans1, std::abs(col[now] + edge[i].val - col[edge[i].to]));//gcd(i,0)=i;所以不影响
        else col[edge[i].to] = col[now] + edge[i].val, DFS(edge[i].to);
    }
}
int main(void)
{
    int a, b;
    in(dot), in(line);
    for (R int i = 1; i <= dot; ++i) bel[i] = i;
    std::memset(mn, 63, sizeof(mn));
    for (R int i = 1; i <= line; ++i)
    {
        in(a), in(b);
        add(a, b, 1), add(b, a, -1);
        bel[find(a)] = bel[find(b)];
    }
    for (R int i = 1; i <= dot; ++i)
    if(!vis[find(i)])
    {
        tar = find(i), DFS(find(i));
        vis[find(i)] = true;
        len += mx[tar] - mn[tar] + 1;
    }
    if(ans1)
    {
        if(ans1 < 3) return printf("-1 -1"), 0;
        for (R int i = 3; i <= ans1; ++i) if(!(ans1 % i)) {ans2 = i; break;}
        printf("%d %d", ans1, ans2);
    }
    else
    {
        if(len < 3) return printf("-1 -1"), 0;
        printf("%d 3", len);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值