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取模的结果
Input
第一行是一个正整数N,表示一共有N支球队。接下来一行N个非负整数,依次表示各队的最后总得分。
输入保证20%的数据满足N≤4,40%的数据满足N≤6,60%的数据满足N≤8,100%的数据满足3≤N≤10且至少存在一组解。
Output
仅包含一个整数,表示答案对10^9+7取模的结果
Sample Input
4 3 6 4
Sample Output
HINT
Source
鬼里鬼气的搜索题,傻逼爆搜就是枚举每场比赛的结果,考虑分数状态有大量重复,考虑用记忆化搜索;
每个人的得分最大27,只有10个人,所以用ll的哈希记录状态用map来判重;
用f[i][S],表示从第i个人开始搜,分数状态为S的方案数;
然后依旧是枚举每个人和他后面的人比赛,有一个剪枝if(3*(r-l+1)<a[i])就是不合法,然后我们从剩余分数小的开始和别人比赛,这样会肯定更快搜完;
我们在搜完一个人的时候就把后面的状态sort一遍再继续搜下一个人,这样不会影响结果,而且排序后会更快搜完;
话说这是cqoi的原题,堂堂HN竟然考他省原题,还没有加强。。。
//MADE BY QT666 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<map> using namespace std; typedef long long ll; const int N=100050; const int mod=1e9+7; map<ll,ll> bj[N],f[N]; int n; ll gethsh(int *a){ ll ret=1; for(int i=1;i<=n;i++) ret=ret*29+a[i]; return ret; } int dfs(int *a,int l,int r){ if(l>=r){ if(a[l]) return 0; if(l==n) return 1; int b[15]; for(int i=1;i<=n;i++) b[i]=a[i]; sort(b+1+l,b+1+n);ll hsh=gethsh(b); if(bj[l+1][hsh]) return f[l+1][hsh]; f[l+1][hsh]=dfs(b,l+1,n); bj[l+1][hsh]=1; return f[l+1][hsh]; } if(3*(r-l+1)<a[l]) return 0; int tot=0; if(a[l]>=3){ a[l]-=3; tot+=dfs(a,l,r-1);tot%=mod; a[l]+=3; } if(a[l]>=1&&a[r]>=1){ a[l]--,a[r]--; tot+=dfs(a,l,r-1);tot%=mod; a[l]++;a[r]++; } if(a[r]>=3){ a[r]-=3; tot+=dfs(a,l,r-1);tot%=mod; a[r]+=3; } return tot; } int a[N]; int main(){ //freopen("match.in","r",stdin); //freopen("match.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int ans=dfs(a,1,n); printf("%d\n",ans); return 0; }