• bzoj4715 囚人的旋律


    4715: 囚人的旋律

    Time Limit: 10 Sec  Memory Limit: 512 MB
    Submit: 74  Solved: 48
    [Submit][Status][Discuss]

    Description

    「不知从何处,流淌出令人熟悉的旋律。
    我到底是在哪里,听过这个旋律?」
    ——「『囚人的旋律』,是加入了诅咒的旋律哦」
    【问题描述】
    被诅咒的监狱里流淌着囚人们的歌谣。
    将罪恶的青春全部抹杀殆尽。
    “看守”执掌“囚犯”的生杀大权。
    “囚犯”中藏着可以杀掉“看守”的恶魔。
    这就是,将人性扭曲的,“监狱游戏”。
     
    监狱游戏的参加者被分为了看守和囚犯,两侧各n人。举行监狱游戏的地点在一个被改造过了的大仓库一样的地方
    ,里面有两排共2n个房间,服务入口侧的是囚犯的房间,行刑室侧的是看守的房间。如下图所示。
     
    相邻的看守与囚犯的房间之间可以通过对讲机互相沟通,但是声音会被处理,无法辨别。两侧的分类房中都有一排
    各n扇门,从左到右编号为1~n。进入一扇门之后会有一条狭长、黑暗,而且弯弯曲曲的走廊通向房间。由于其特
    殊的构造,看守的i号门对应房间未必就是囚犯的i号门对应的房间。因此,想在这个监狱游戏中胜出,了解门与门
    之间的对应关系是很有必要的。接下来的问题就和监狱游戏没有太多关系了。我们令a[i]表示看守的第i扇门对应
    囚犯的哪一扇门。令图G为有n个节点的图,编号为1~n。对于满足1≤i<j≤n的一对i和j,如果有a[i]>a[j],那么
    在G中编号为i和j的节点之间连一条边。得到的图G被称为逆序图。对于图G=(V,E),非空点集S∈V是一个独立集当
    且仅当对于任意两个点u,v∈V,不存在(u,v)∈E。而S是一个覆盖集当且仅当对于任意点v?S存在点u∈S满足(u,v)
    ∈E。我们在意的是,图G中有多少个点集既是独立集又是覆盖集。出于某种不知名的原因,被迫参加监狱游戏的大
    家的安危和这个问题的答案有关。拜托了,请一定要求出这个方案数。

    Input

    输入第一行含有两个整数n和m,表示逆序图的点数和边数。
    接下来m行,每行描述一条边。每行包含两个1~n的整数,代表边的两个端点。保证没有重边和自环。
    保证给定的图是一个合法的逆序图,即,存在至少一个序列,按照题目描述中所述方法得到的逆序图是给定的图。
    n≤1000,0≤m≤(n(n-1))/2

    Output

    输出一个整数,表示方案数对1,000,000,007取模得到的结果。

    Sample Input

    5 5
    2 4
    2 5
    1 4
    3 4
    3 5

    Sample Output

    3
    分析:在图上求独立集与覆盖集好像是没有多项式解法的. 既然要求方案数,那么肯定要用到dp或数学方法. 因为逆序图和序列是一一对应的,所以可以先把逆序图转化为序列,在序列上来处理.
       图中编号为 i 的节点即为序列中的第 i 个元素,一条边则代表一个逆序对。那么点 i 与所有编号大于 i 的点之间连的边数,就是序列中第 i 个数后面比 i 小的元素的个数。
       于是我们就得到了一个算法。从左到右考虑序列的每一个元素 i,求出点 i 与编号大于i 的点之间连的边数,假设为 k,那么 a[i] 就是当前尚未被选择的第 k + 1 小的数。这个算法的复杂度为O(n^2).
       怎么求方案数呢?
       令f[i]表示以i结尾的符合要求的子序列有多少个. f[i] += f[j] (j < i). 必须满足要求:a[j] < a[i],a[k] < a[j]或a[k] > a[i](j < k < i). 这样元素i,j在图上就既是独立集,也是覆盖集了. 
       这样复杂度是O(n^3)的,怎么优化呢? 固定左端点i,动态维护a[k]的最小值,往右枚举a[j]即可.
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 1010,mod = 1e9+7;
    int n,m,vis[maxn],du[maxn],a[maxn],f[maxn];
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i = 1; i <= m; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if (x < y)
                du[x]++;
            else
                du[y]++;
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
            {
                if (vis[j])
                    continue;
                if (!du[i])
                {
                    a[i] = j;
                    vis[j] = 1;
                    break;
                }
                du[i]--;
            }
        a[0] = 0;
        a[n + 1] = n + 1;
        f[0] = 1;
        for (int i = 0; i <= n; i++)
        {
            int temp = n + 1;
            for (int j = i + 1; j <= n + 1; j++)
            {
                if (temp < a[j] || a[i] > a[j])
                    continue;
                f[j] += f[i];
                f[j] %= mod;
                temp = min(temp,a[j]);
            }
        }
        cout << f[n + 1] << endl;
    
        return 0;
    }
     
  • 相关阅读:
    对MVC模型的自悟,详尽解释,为了更多非计算机人员可以理解
    openSUSE leap 42.3 实现有线 无线同时用
    Fedora27 源配置
    Ubuntu16.04添加HP Laserjet Pro M128fn打印机和驱动
    openSUSE leap 42.3 添加HP Laserjet Pro M128fn打印机和驱动
    OpenSUSE Leap 42.3下通过Firefox Opera Chromium浏览器直接执行java应用程序(打开java jnlp文件)实现在服务器远程虚拟控制台完成远程管理的方法
    OpenSUSE Leap 42.3 安装java(Oracle jre)
    linux下支持托盘的邮件客户端Sylpheed
    Ubuntu下通过Firefox Opera Chromium浏览器直接执行java应用程序(打开java jnlp文件)实现在服务器远程虚拟控制台完成远程管理的方法
    Firefox 浏览器添加Linux jre插件
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8707297.html
Copyright © 2020-2023  润新知