洛谷P1477 bzoj1064 [NOI2008]假面舞会

P1477 [NOI2008]假面舞会

题目描述

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

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

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

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

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

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

输入输出格式

输入格式:

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

 

输出格式:

包含两个数,第一个数为最大可能的面具类数,第二个数为最小可能的面具类数。如果无法将所有的面具分为至少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%的数据,满足n ≤ 300, m ≤ 1000;

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

 

这道题告诉我的惨痛教训 每次挖挖之后一定要看自己空间到底开够没有!!! qswl

这道题是一道很神奇的图论题

首先看到这个关系首先想到把它们建边 这道题主要是分为三种情况

1.环  2.奇奇怪怪的环  3.链

先说说链的情况 如果有环 那么类别数会被环限制 这时候链就没用了 如果没有环 他的类别数就是所有的链长度之和 相当于把所有链的首尾连到一起

接着是环 出现环也就是说总类别数进行了若干次循环 且一定是每个换的因子  所以最大情况数就取所有环的$gcd$即可

最大的问题是奇奇怪怪的环 长这样子的

因为起点相同 所以图中同种颜色的是一个类别的 那么蓝色点相当于和后面两个白点重新成环 为一个三个点的环

所以这种情况怎么处理呢 我们建立边权为$1$的正边 $-1$的反边 那么他走一圈走回蓝点时 要继续往回走就只能走反边

也就是说反边将类别相同的点抵消了 留下了剩余的新环 最后特判一下无解的情况即可

代码

#include <bits/stdc++.h>
#define oo 1e9
using namespace std;

const int N = 1e6 + 5;
int head[N], nex[2 * N], tov[2 * N], val[2 * N];
int vis[N], tot, n, m, sum;
queue<int>Q;
vector<int>R;

void add(int u, int v, int w) {
    
    tot ++;
    nex[tot] = head[u];
    tov[tot] = v;
    val[tot] = w;
    head[u] = tot;
}

void Add_Edge( ) {
    
    scanf("%d%d",& n,& m);
    for(int i = 1;i <= m;i ++) {
        int u, v;
        scanf("%d%d",& u,& v);
        add(u, v, 1); add(v, u, -1);
    }
}

void bfs(int u) {
    
    vis[u] = oo; Q.push(u); bool tag = false;
    int ma = oo, mi = oo;
    while(! Q.empty( )) {
        int u = Q.front( ); Q.pop( );
        for(int i = head[u];i;i = nex[i]) {
            int v = tov[i];
            if(! vis[v]) {
                vis[v] = vis[u] + val[i];
                Q.push(v); 
                ma = max(ma, vis[v]); mi = min(mi, vis[v]);
            }
            else if(vis[v] == vis[u] + val[i]) continue;
            else {
                tag = true;
                R.push_back(abs(vis[v] - (vis[u] + val[i])));
            }
        }
    }
    if(! tag) sum += ma - mi + 1;
}

int gcd(int a, int b) {
    
    return b == 0 ? a : gcd(b, a % b);
}

void Solve( ) {
    
    for(int i = 1; i <= n; i++) {
        if(! vis[i]) bfs(i);
    }
    if(R.size( )) {
        int ans = R[0];
        for(int i = 1;i < R.size( );i ++) {
            ans = gcd(ans, R[i]);
        }
        if(ans <= 2) {
            printf("-1 -1\n"); return ;
        }
        else {
            int anss = ans;
            for(int i = 2;i * i <= ans;i ++) {
                if(ans % i == 0) {
                    if(i >= 3) anss = min(anss, i);
                    if(ans / i >= 3) anss = min(anss, ans / i); 
                }
            }
            printf("%d %d\n", ans, anss);
        }
    }
    else {
        if(sum <= 2) printf("-1 -1\n");
        else printf("%d %d", sum, 3);
    }
}

int main( ) {
    
    Add_Edge( );
    Solve( );
}
posted @ 2018-10-12 09:29  阿澈说他也想好好学习  阅读(149)  评论(0编辑  收藏  举报