队伍统计

Posted mocking-jimmy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了队伍统计相关的知识,希望对你有一定的参考价值。

1月27日

Description

现在有n个人要排成一列,编号为1->n 。但由于一些不明原因的关系,人与人之间可能存在一些矛盾关系,具体有m条矛盾关系(u,v),表示编号为u的人想要排在编号为v的人前面。要使得队伍和谐,最多不能违背k条矛盾关系(即不能有超过k条矛盾关系(u,v),满足最后v排在了u前面)。问有多少合法的排列。答案对 (10^9+7) 取模。

Input

输入文件名为count. in。
第一行包括三个整数n,m,k。
接下来m行,每行两个整数u,v,描述一个矛盾关系(u,v)。
保证不存在两对矛盾关系(u,v),(x,y),使得u=x且v=y 。

Output

输出文件名为count.out。
输出包括一行表示合法的排列数。

Sample Input

输入1:

4 2 1
1 3
4 2

输入2:

10 12 3
2 6
6 10
1 7
4 1
6 1
2 4
7 6
1 4
10 4
10 9
5 9
8 10

Sample Output

输出1:

18

输出2:

123120

Data Constraint

对于30%的数据,(nleq 10)

对于60%的数据,(nleq 15)

对应100%的数据,n, (kleq 20) ,(mleq n imes (n-1)),保证矛盾关系不重复。

Solution

  • 【我的解法】

无脑暴力,30分,可以欣赏一下

#include<cstdio>
#pragma GCC optimize(3)//咳咳,别管这是什么
using namespace std;
const int mod=1000000007;

int n,m,k,tot;
int g[21][21];
int a[21];
bool vis[21];

void dfs(int x)
{
    if(x>n)
    {
        int cnt=0;
        for(int i=1;i<n;++i)
            for(int j=i+1;j<=n;++j)
                cnt+=g[a[i]][a[j]];
        if(cnt<=k)
        {
            tot++;
            tot%=mod;
        }
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i]) continue;
        a[x]=i;
        vis[i]=true;
        dfs(x+1);
        vis[i]=false;
    }
}

int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        g[v][u]=1;
    }
    dfs(1);
    printf("%d",tot);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 【大佬正解】

首先把状态压缩一下,把队伍压缩成一个二进制,由于n最大只有20,所以 (2^{20}) 是不会MLE的。
然后从前往后递推。f(s,i)表示当前选了s集合这些人,i表示违反了多少条矛盾关系。
然后如果暴力转移的话,是会炸的。 (O(2^nn^2k))。所以要进行一些非(sang)常(xin)基(bing)础(kuang)的优化。
因为矛盾是没有重复的,所以把要在x前面的连个链式前向星,然后往左位移再&一下就可以了。

(其实有更优方法,但我不会调。。。只要维护一个fal[x]表示要排在x后面的人的集合,那么每次只要求出s & fal[x]的1的个数就能快速更新??了。)

#include<cstdio>
//#pragma GCC optimize(3)//不敢了不敢了再也不敢吸臭氧了
#define mod 1000000007
using namespace std;

int n,m,k,tot,ans;
int f[2097153][21];
int head[21],to[401],next[401],dive;

void add(int x,int y)
{
    ++dive;
    next[dive]=head[x];
    to[dive]=y;
    head[x]=dive;
}
inline int read()//不吸臭氧就只能快读了QAQ
{
    int x=0; char c=getchar();
    while (c<‘0‘ || c>‘9‘) c=getchar();
    while (c>=‘0‘ && c<=‘9‘)
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
}

int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    n=read();
    m=read();
    k=read();
    for(int i=1,u,v;i<=m;i++)
    {
        u=read();
        v=read();
        add(v,u);
    }
    f[0][0]=1;
    tot=(1<<n)-1;
    for(int s=0;s<=tot;s++)
        for(int i=0;i<=k;i++)
        {
            if(!f[s][i]) continue;
            for(int x=1;x<=n;x++)
            {
                if(s&(1<<x-1)) continue;
                int c=0;
                for(int p=head[x];p;p=next[p])
                    if(!(s&(1<<to[p]-1))) c++;
                if(i+c>k) continue;
                f[s|(1<<x-1)][i+c]+=f[s][i];
                f[s|(1<<x-1)][i+c]%=mod;
            }
        }
    for(int i=0;i<=k;i++)
    {
        ans+=f[tot][i];
        ans%=mod;
    }
    printf("%d
",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}







以上是关于队伍统计的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2017模拟8.5队伍统计

jzoj 5230_队伍统计_dp

c_cpp 快速代码片段,用于在统计(阻止)/ dev / rdsk中的设备时验证fstat64和stat64的行为。

排球积分程序

第十八周 个人作业

第十六届全国大学生智能车竞赛报名信息统计