[题解] BZOJ 3139 [HNOI2013] 比赛

题目描述 Description
沫沫非常喜欢看足球赛,但因为沉迷于射箭游戏,错过了最近的一次足球联赛。此次联赛共&N&支球队参加,比赛规则如下:
(1) 每两支球队之间踢一场比赛。
(2) 若平局,两支球队各得1分。
(3) 否则胜利的球队得3分,败者不得分。
尽管非常遗憾没有观赏到精彩的比赛,但沫沫通过新闻知道了每只球队的最后总得分,
然后聪明的她想计算出有多少种可能的比赛过程。
譬如有3支球队,每支球队最后均积3分,那么有两种可能的情况:

但沫沫发现当球队较多时,计算工作量将非常大,所以这个任务就交给你了。请你计算出可能的比赛过程的数目,由于答案可能很大,你只需要输出答案对 10 9 + 7 取模的结果。

输入描述 Input Description
第一行是一个正整数 N ,表示一共有 N 支球队。
接下来一行 N 个非负整数,依次表示各队的最后总得分。

输出描述 Output Description
仅包含一个整数,表示答案对 10 9 + 7 取模的结果。

样例输入 Sample Input
4
4 3 6 4

样例输出 Sample Output
3

数据范围及提示 Data Size & Hint
20%的数据满足 N 4
40%的数据满足 N 6
60%的数据满足 N 8
100%的数据满足 3 N 10 且至少存在一组解。

Solution

(前言) 每支球队对我们来说在本质上都是一样的,其顺序随便安排就好

我们直接将一个队伍和其他队伍的所有比赛情况搜完,当然要使当前的队伍剩余得分为0才行,因为它不可能再和别的队伍比完赛了
然后剩下 n 1 个队伍,按照上面的操作继续递归搜索就好了

显然,这样的时间复杂度是我们所不能接受的,那么如何优化?
显然是可以用记忆化的
可以将处理完 i 个队伍后剩下 n i 个队伍剩余得分的情况记录下来,当下次处理 i 个队伍并且剩下队伍剩余得分情况与这次相同时就直接调用结果即可

但是剩余得分情况可能会看起来不相同但是本质上一样,比如说
1 2 3 4
4 3 2 1
emm,这样的状态怎么记录呢?

看到前言部分,每支球队在本质上是一样的,所以强行按照从大到小的顺序给它排序再利用28进制暴力储存在一个long long里面(最大 27 10 ),再用hash或者map映射一下,完美!

但是若当前处理到的队伍不一样(这次已经处理到第i个了而上次的情况才处理到第j个)但是剩余队伍的得分剩余情况相同怎么办?这样不就会直接调用上次的结果吗?但实际上是不能用的呀!

解决办法是可以多压一个当前处理的位数进去,这样就保证了不重复,不漏加

爆搜大法吼啊

代码如下

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define hmod 840023
#define ll long long
ll tot;
int n,a[11],b[11];

struct H {
    ll val[850010];
    ll num[850010];
    bool use[850010];
    int hash(ll x) {
        int now=x%hmod;
        while(use[now] && num[now]!=x) now=(now+17)%hmod;
        return now;
    }
}h;
bool cmp(int a,int b) {return a>b;}
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
ll dfs(int x,int y) {
    if(a[x]>3*(n-y+1)) return 0;
    if(y>n) {
        b[0]=x;
        for(int i=1;i<=n;i++)
            b[i]=a[i];
        sort(b+x+1,b+n+1,cmp);
        ll sum=0;
        for(int i=0;i<=n;i++)
            sum=sum*28+b[i];
        int now=h.hash(sum);
        if(h.use[now]) {return h.val[now];}
        else {
            h.use[now]=1;
            h.num[now]=sum;
            return h.val[now]=dfs(x+1,x+2); 
        }
    }
    ll res=0,tmp=0;
    if(a[x]>=3) {
        a[x]-=3;
        tmp=dfs(x,y+1);
        res=(res+tmp)%mod;
        a[x]+=3;
    }
    if(a[x]>=1 && a[y]>=1) {
        a[x]-=1; a[y]-=1;
        tmp=dfs(x,y+1);
        res=(res+tmp)%mod;
        a[x]+=1; a[y]+=1;
    }
    if(a[y]>=3) {
        a[y]-=3;
        tmp=dfs(x,y+1);
        res=(res+tmp)%mod;
        a[y]+=3;
    }
    return res;
}
int main() {
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    sort(a+1,a+n+1,cmp);
    ll sum=(ll)(n-1)*pow(28,n);
    int now=h.hash(sum);
    h.num[now]=sum;
    h.val[now]=h.use[now]=1;
    //这是将刚好处理完第n-1个然后所有的剩余队伍分数都为0的情况
    printf("%lld\n",dfs(1,2));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/force_chl/article/details/79660867