Description
沫沫非常喜欢看足球赛,但因为沉迷于射箭游戏,错过了最近的一次足球联赛。此次联 赛共N支球队参加,比赛规则如下:
(1) 每两支球队之间踢一场比赛。 (2) 若平局,两支球队各得1分。
(3) 否则胜利的球队得3分,败者不得分。
尽管非常遗憾没有观赏到精彩的比赛,但沫沫通过新闻知道了每只球队的最后总得分, 然后聪明的她想计算出有多少种可能的比赛过程。
譬如有3支球队,每支球队最后均积3分,那么有两种可能的情况:
可能性1 可能性2
球队 | A | B | C | 得分 | 球队 | A | B | C | 得分 |
---|---|---|---|---|---|---|---|---|---|
A | - | 3 | 0 | 3 | A | - | 0 | 3 | 3 |
B | 0 | - | 3 | 3 | B | 3 | - | 0 | 3 |
C | 3 | 0 | - | 3 | C | 0 | 3 | - | 3 |
但沫沫发现当球队较多时,计算工作量将非常大,所以这个任务就交给你了。请你计算 出可能的比赛过程的数目,由于答案可能很大,你只需要输出答案对109+7取模的结果
Input
第一行是一个正整数N,表示一共有N支球队。 接下来一行N个非负整数,依次表示各队的最后总得分。
输入保证20%的数据满足N≤4,40%的数据满足N≤6,60%的数据满足N≤8,100%的数据 满足3≤N≤10且至少存在一组解。
Output
仅包含一个整数,表示答案对10^9+7取模的结果
Sample Input
4
4 3 6 4
Sample Output
3
题解
每个队是无差别的,那么我们排序之后处理。
对于每个队,搜索他与后面所有队伍的比赛情况。
当然当前队的之前的每个队伍都已经搜索完毕,我们采用在最终得分的基础上减分的方式,那么他们的分数都应该是0了。我们考虑将剩下的还没计算完毕的队伍现在的得分hash一下——因为一个队伍最高的得分就只有27,那么10支队伍用18进制也并不超long long——用map实现记忆化搜索,那么这样就可以通过此题了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1000000000 + 7, N = 15, P = 28;
int n, a[N], b[N];
map<ll, ll> f[N];
void init(){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
}
ll dfs(int);
ll cal(int u, int v, int x){
if(v > n){
if(x != 0) return 0;
else return dfs(u + 1);
}
if(3 * (n - v + 1) < x) return 0;
ll cnt = 0;
if(x >= 3) cnt = (cnt + cal(u, v + 1, x - 3)) % mod;
if(x >= 1 && a[v] >= 1){
a[v]--;
cnt = (cnt + cal(u, v + 1, x - 1)) % mod;
a[v]++;
}
if(a[v] >= 3){
a[v] -= 3;
cnt = (cnt + cal(u, v + 1, x)) % mod;
a[v] += 3;
}
return cnt;
}
ll dfs(int x){
if(x == n) return a[n] == 0;
int top = 0;
for(int i = x; i <= n; i++) b[++top] = a[i];
sort(b + 1, b + top + 1);
ll hs = 0;
for(int i = 1; i <= top; i++) hs = hs * P + b[i];
if(f[x].find(hs) != f[x].end()) return f[x][hs];
else return f[x][hs] = cal(x, x + 1, a[x]);
}
void work(){
printf("%lld\n", dfs(1));
}
int main(){
freopen("bzoj3139.in", "r", stdin);
freopen("bzoj3139.out", "w", stdout);
init();
work();
return 0;
}