话说好久没写题(解)了。。
先贴份题解:http://wjmzbmr.com/archives/hnoi-2013-%E9%A2%98%E8%A7%A3/(LJ神题解。。Lazycal表示看不懂。。)
以下是Lazycal's题解:
[bzoj3139][Hnoi2013]比赛
对于一个得分序列,可以发现不论如何排列,答案都是一样的。而且n的得分序列可以由n-1的推来。于是,我们可以搜索第一个队伍与其他队伍的比赛结果,由比赛结果得出n-1支队伍的得分序列,递归搜索。然后用每个队伍的得分和队伍个数作为状态,对每个状态进行压缩,利用最小表示法。由于每个队伍最多只能得27分,所以状态总数不超过28^9/(9!)*2(因为只能存在一个0)为千万级别,可以接受。
/**************************************************************
Problem: 3139
User: lazycal
Language: C++
Result: Accepted
Time:732 ms
Memory:1868 kb
****************************************************************/
#include <cstdio>
#include <algorithm>
#include <functional>
#include <map>
const int MOD=1000000000+7;
int n;
std::map<long long,long long>Map;
struct Status
{
int a[11];
long long hash()
{
long long res=0;
for (int i=0;i<=n;++i) res=res*28+a[i];
return res;
}
void sort()
{
std::sort(a+n-a[0]+1,a+1+n/*,std::greater<int>()*/);
}
}start,bound;
long long dfs(const int step,Status now)
{
if (now.a[0] == 1) return Map[now.hash()]=-1;
if (now.a[n-now.a[0]+1] > 3*(n-step+1)) return -1;
if (step > n) {
--now.a[0];
now.sort();
if (Map[now.hash()]) return Map[now.hash()];
return dfs(n-now.a[0]+2,now);
}
long long res=0,tmp;
int idx=n-now.a[0]+1;
if (now.a[idx] >= 3) {
now.a[idx] -= 3;
tmp=dfs(step+1,now);
if (tmp!=-1) (res+=tmp)%=MOD;
now.a[idx] +=3;
}
if (now.a[idx] >= 1 && now.a[step] >= 1) {
now.a[idx] -= 1; now.a[step] -= 1;
tmp=dfs(step+1,now);
if (tmp!=-1) (res+=tmp)%=MOD;
now.a[idx] += 1; now.a[step] += 1;
}
if (now.a[step] >= 3) {
now.a[step] -= 3;
tmp=dfs(step+1,now);
if (tmp!=-1) (res+=tmp)%=MOD;
now.a[step] += 3;
}
res=res?res:-1;
if (step == idx+1) Map[now.hash()]=res;
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("3139.in","r",stdin);
freopen("3139.out","w",stdout);
#endif
scanf("%d",&n);
if (n==1) {puts("1");return 0;}
for (int i=1;i<=n;++i) scanf("%d",start.a+i);
start.a[0]=n;
start.sort();
bound.a[0]=1; bound.a[n]=0; Map[bound.hash()]=1;
printf("%lld\n",dfs(2,start));
}