bzoj 3139: [Hnoi2013]比赛
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取模的结果
solution
我是个xlb,这个题目显然需要记忆化,很久以前yy出hash DP一直没用得上,这题显然可以用,我的转移十分作死,转移是唯一的,记忆化没有什么用,其实只需要hash后面的队伍即可,其他基础剪枝看代码,另外队伍是等价的,所以拥有相同剩余分数的队伍是一样的,我们可以默认分数从小到大排序,这样可以减少状态
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <map>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=15,mod=1e9+7;
int n,a[N],m=0,res[N];
struct node{
int x,y;
}e[N*N];
map<ll,ll>s;
long long ans=0;
il bool check(){
for(RG int i=1;i<=n;i++)
if(a[i])return false;
return true;
}
il bool judge(){
for(RG int i=1;i<=n;i++)
if(3*res[i]<a[i])return true;
return false;
}
int b[N];
il ll query(int x){
ll tot=x;int l=0;
for(int i=x+1;i<=n;i++)b[++l]=a[i];
sort(b+1,b+l+1);
for(int i=1;i<=l;i++)
tot=tot*28+b[i];
return tot;
}
il int dfs(int i){
if(i==m+1){
if(check())return 1;
return 0;
}
if(judge())return 0;
ll li;RG int x=e[i].x,y=e[i].y;ll ans=0;
if(e[i].x!=e[i-1].x){
li=query(e[i-1].x);
if(s.count(li))return s[li];
}
res[x]--;res[y]--;
if(a[x]>=3)a[x]-=3,ans+=dfs(i+1),a[x]+=3;if(ans>=mod)ans-=mod;
if(a[y]>=3)a[y]-=3,ans+=dfs(i+1),a[y]+=3;if(ans>=mod)ans-=mod;
if(a[x] && a[y])a[x]--,a[y]--,ans+=dfs(i+1),a[x]++,a[y]++;
res[x]++;res[y]++;
if(ans>=mod)ans-=mod;
if(e[i].x!=e[i-1].x)s[li]=ans;
return ans;
}
void work()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
e[++m].x=i,e[m].y=j;
for(int i=1;i<=n;i++)res[i]=n-1;
cout<<dfs(1)<<endl;
}
int main(){work();return 0;}