zoukankan      html  css  js  c++  java
  • [NOI2008] 假面舞会

    [NOI2008] 假面舞会

    题目大意:有(n)个人,每个人都有一个标号,每种标号的人只能看见下一个标号的人(最后一种标号看见第一种),给定(m)个关系,求这个关系是否合法以及合法情况下最大和最小的方案数,要求方案数不能小于(3)(n⩽10^5),$m⩽10^6 $

    Solution

    用染色的思想;关系构成有向图,图的样式要么是环形结构,要么是树形结构

    • 考虑一条链,可以任意层数,所以如果图中只有链或树,取最长链作为(ans)

      链

    • 考虑环,因为链可以任意层数,那么有了环后,答案就是(gcd(ans, 环长)),当然在此,环指的是环状结构,有下面两种构造,建权值为(-1)的反向边可以解决第一种构造

    • 如果是很多环呢?

    Code

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using std::max;
    using std::min;
    
    const int N = 100005;
    const int M = 1000005;
    
    int head[N], p[N], maxx[N], minn[N], vis[N];
    int cnt, tmp, ans, ans2, n, m;
    
    struct Edge{
        int to, val, next;
    }e[M << 1];
    
    inline void addedge(int x, int y, int z){
        ++cnt;
        e[cnt].to = y;
        e[cnt].next = head[x];
        e[cnt].val = z;
        head[x] = cnt;
    }
    
    int gcd(int a, int b){
        if(b == 0)
            return a;
        else return gcd(b, a % b);
    }//欧几里得算法
    
    void dfs(int x){
        maxx[tmp] = max(maxx[tmp], p[x]), minn[tmp] = min(minn[tmp], p[x]);//记录一个联通块的链长,即标号的差 + 1
        vis[x] = true;
        for(int i = head[x], v; i; i = e[i].next){
            v = e[i].to;
            if(vis[v]){
                ans = gcd(ans, abs(p[x] + e[i].val - p[v]));//一个环,首尾标号差的绝对值//不应该在这里+1,画图可知,但是要在统计链的时候+1 
            }else{
                p[v] = p[x] + e[i].val;//标号,可能是反向边
                dfs(v);
            }
        }
    }
    int main(){
        scanf("%d %d", &n, &m);
        for(int i = 1, u, v; i <= m; ++i){
            scanf("%d %d", &u, &v);
            addedge(u, v, 1);
            addedge(v, u, -1);
        }
        for(int i = 1; i <= n; ++i){
            if(!vis[i]){
                minn[i] = 19260817;//初始化最小标号
                p[i] = 1;
                tmp = i;
                dfs(i);
            }
        }
        if(ans >= 3){
            for(int i = 3; i <= ans; ++i){
                if(ans % i == 0){
                    ans2 = i;
                    break;
                }
            }
            printf("%d %d
    ", ans, ans2);
            return 0;
        }else if(ans == 0){
            for(int i = 1; i <= n; ++i){
                if(maxx[i] || minn[i]){
                    ans = ans + (maxx[i] - minn[i] + 1);
                    
                }
            }
            if(ans >= 3){
                printf("%d 3
    ", ans);
                return 0;
            }else {
                printf("-1 -1
    ");
                return 0;
            }
        }
        /*统计答案的思路:
       如果有环了,且ans >= 3 那么我们取最小答案为它大于等于3的第一个因子
       如果有环且ans < 3, 说明不行
       如果没有环,统计最长链,ans2 = 0,如果最长链 < 3 不合法
        */
        printf("-1 -1
    ");
        return 0;
    }
    
  • 相关阅读:
    详解MySQL中EXPLAIN解释命令(转)
    yii framework config 可以被配置的项目
    YII之yiic创建YII应用
    Yii 用户登录验证
    全选,反选,获取值ajax提交
    firefox常用插件
    file_get_contents failed to open stream: HTTP request failed(一个字符决定成败)
    linux svn服务器(一)
    js操作cookie(转载:经测试可用)
    淘宝用户api 如何获得App Key和API Secret
  • 原文地址:https://www.cnblogs.com/LMSH7/p/9564178.html
Copyright © 2011-2022 走看看