zoukankan      html  css  js  c++  java
  • 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;
    }
     
  • 相关阅读:
    Lucene:(一)建立索引文件:2。建立索引文件(一)
    Lucene:(一)建立索引文件:2。建立索引文件(二)Segment文件
    92.外边距设置 Walker
    99.元素居中及样式重置 Walker
    94.外边距踩坑 Walker
    101.列表属性 Walker
    97.boxsizing属性 Walker
    98.溢出隐藏 Walker
    95.内边距设置 Walker
    96.内边距和边框踩坑 Walker
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8707297.html
Copyright © 2011-2022 走看看