博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bzoj4498: 魔法的碰撞
阅读量:5218 次
发布时间:2019-06-14

本文共 2594 字,大约阅读时间需要 8 分钟。

好题实吹 这种排列计数问题确实感觉无从下手啊

考虑暴力,是枚举每一个排列,排列的贡献为C(L-W,n),其中W是max(di-1,di)-1,即相邻两个魔法师之间不能放的位置

考虑到攻击范围比较小,一个人最多打80个位置,假如能够计算出对于每个W的排列方案数,就能算出答案了

排序消掉max,按大到小插入魔法师,那么对于当前魔法师,就有三种情况:

1、自己找个地方随便放,那么最后它对不能够放的位置的贡献就是2*(di-1)

2、放在其中一个魔法师旁边,贡献为di-1

3、放在两个魔法师之间,贡献为0

但是直接这样算方案的话,插入的魔法师会分成很多段,状态就很难表示了

考虑到我们并不关心魔法师具体的位置或者相对位置,只关心他打了多少位置

我们再弄一个关键,目前还剩下的空位

什么鬼?就是每次插入魔法师的时候,先想好未来让不让一个魔法师放到自己的左/右边,因为不关心位置所以位置可以直接+0,+1,或+2,当然插入进来也会用1个位置

这样就可以DP了,设f[i][j][k]表示前i个人,有多少位置可以插入,当前能打多少位置

注意假如+1,方案数要乘2,因为可以放左也可以放右

我预处理阶乘的逆元比较蠢,每个都求了一次,实际上把最后一个算出来不断往前乘就好了

 

upd:这里给出另一种做法,或许更好理解些

前面的都一样,到分成很多段那儿,我们可以设分成了多少段

设f[i][j][k][p]表示前i个人,分成了j段,能打了k个位置,p=0/1/2意思是左界和右界共到了多少个

思路是把两段合并,还是接在一段一边,还是插在某两段中间不相连

#include
#include
#include
#include
#include
#include
using namespace std;typedef long long LL;const int _=1e2;const int maxn=40+10;const int maxd=40+10;const int maxL=1e6+_;const LL mod=1e9+7;LL quick_pow(LL A,LL p){ LL ret=1; while(p!=0) { if(p%2==1)ret=ret*A%mod; A=A*A%mod;p/=2; } return ret;}LL fac[maxL],fac_inv[maxL];void yu(int L){ fac[0]=1,fac_inv[0]=1; for(int i=1;i<=L;i++) fac[i]=fac[i-1]*i%mod,fac_inv[i]=quick_pow(fac[i],mod-2);}LL C(int n,int m){ return fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;}int d[maxn];LL f[maxn][maxn][2*maxn*maxd];int main(){ freopen("a.in","r",stdin); freopen("a.out","w",stdout); int L,n,li=0; scanf("%d%d",&L,&n); for(int i=1;i<=n;i++) scanf("%d",&d[i]),d[i]--,li+=2*d[i]; sort(d+1,d+n+1); reverse(d+1,d+n+1); f[0][1][0]=1; for(int i=0;i

 

#include
#include
#include
#include
#include
#include
using namespace std;typedef long long LL;const int _=1e2;const int maxn=40+10;const int maxd=40+10;const int maxL=1e6+_;const LL mod=1e9+7;LL quick_pow(LL A,LL p){ LL ret=1; while(p!=0) { if(p%2==1)ret=ret*A%mod; A=A*A%mod;p/=2; } return ret;}LL fac[maxL],fac_inv[maxL];void yu(int L){ fac[0]=1,fac_inv[0]=1; for(int i=1;i<=L;i++) fac[i]=fac[i-1]*i%mod,fac_inv[i]=quick_pow(fac[i],mod-2);}LL C(int n,int m){ return fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;}int d[maxn];LL f[2][maxn][2*maxn*maxd][3];//枚举到第i个位置,有j段,覆盖长度为k,其中挨了多少边界int main(){// freopen("a.in","r",stdin);// freopen("a.out","w",stdout); int L,n,li=0; scanf("%d%d",&L,&n); for(int i=1;i<=n;i++) scanf("%d",&d[i]),d[i]--,li+=2*d[i]; sort(d+1,d+n+1); reverse(d+1,d+n+1); int now=0; f[now][1][2*d[1]][0]=1; f[now][1][d[1]][1]=2; for(int i=1;i

 

转载于:https://www.cnblogs.com/AKCqhzdy/p/10437815.html

你可能感兴趣的文章
Silverlight 的菜单控件。(不是 Toolkit的)
查看>>
:hover 鼠标同时触发两个元素变化
查看>>
go语言学习十三 - 相等性
查看>>
Idea 提交代码到码云(提交到github也大同小异)
查看>>
c#连接excel2007未安装ISAM解决
查看>>
Mono 异步加载数据更新主线程
查看>>
初识lua
查看>>
我是插件狂人,jDuang,jValidator,jModal,jGallery
查看>>
张季跃 201771010139《面向对象程序设计(java)》第四周学习总结
查看>>
如何解除循环引用
查看>>
android中fragment的使用及与activity之间的通信
查看>>
字典【Tire 模板】
查看>>
jquery的contains方法
查看>>
python3--算法基础:二分查找/折半查找
查看>>
Perl IO:随机读写文件
查看>>
Perl IO:IO重定向
查看>>
转:基于用户投票的排名算法系列
查看>>
WSDL 详解
查看>>
[转]ASP数组全集,多维数组和一维数组
查看>>
C# winform DataGridView 常见属性
查看>>