LOJ6374[SDWC2018 Day1]网格

LOJ6374[SDWC2018 Day1]网格

题面:LOJ

解析

这是大佬的题解

先考虑没有任何限制的情况,发现可以将两维分开来看,考虑容斥解决掉步长限制,设\(g(i)\)表示至少\(i\)步超过限制的方案数,那么有:
\[g(i)={R \choose i}{T_x-i\times(M_x+1)+R-1 \choose R-1}\]
就是保证\(i\)步超出限制,然后剩下的步数随意分配即可。
那么容斥可得:
\[Ans=\sum_{i=0}^{R}(-1)^{i} g(i)\]
那么我们可以将二维答案合并起来,就有:\(Ans=Ans_x\times Ans_y\)
但是这样的答案中有停留在原地的步数,所以其实是至多走了\(R\)步的方案数。
再一次考虑容斥消去影响,设\(g(i)\)表示至多走\(i\)步的方案数,\(f(i)\)表示恰好走\(i\)步的方案数,那么有:
\[g(R)=Ans_x\times Ans_y=\sum_{i=0}^{R} {R \choose i}f(i)\]
二项式反演,得:
\[f(R)=\sum_{i=0}^{R}(-1)^{R-i}{R \choose i}g(i)\]
那么分别计算\(g(i)\)统计答案即可。
但是题目中还有其他限制,不能走某些步长,再一次考虑容斥统计答案,设\(g(i)\)表示至少违规移动\(i\)步的方案数,\(f(i)\)表示恰好违规移动\(i\)步的方案数,那么有:
\[Ans=f(0)=\sum_{i=0}^{lim}(-1)^{i}g(i)\]
考虑如何计算\(g(i)\):因为是至少,大概就是强制\(i\)步违规乘上剩下步数随意走的方案数,发现随意走的方案数可以用上面的方法\(O(R^{2})\)计算,我们现在只用考虑如何计算强制\(i\)步违规的方案数了,设\(S(i,j)\)表示违规走\(i\)步,违规走的步长和为\(j\)的方案数,发现\(j\)\(G\)的倍数,因此可以记录\(\frac{j}{G}\),而最多违规移动的步数最多\(lim=\frac{1e6}{1e4}=100\),这样\(S(i,j)\)的状态就只有\(O(lim^2)\)种,可以\(O(lim^3)\)提前预处理出来。
那么有:
\[g(i)=\sum_{j}S(i,j)\times Calc(T_x-j\times G,T_y-j\times G,Mx,My,R-i){R \choose i}\]
计算出\(g(i)\)就可以算出真正的答案了。复杂度是多少呢?
发现\(g(i)\)最多有\(O(lim)\)种,而计算一次\(g(i)\)的复杂度为\(O(lim\times i^2)\),那么总复杂度就是\(O(lim^2 R^2)\)PS:这复杂度感觉过不了啊,常数小?如果算错了,请大佬指出错误。

代码


#include<cstdio>
#include<algorithm>
using namespace std;
const int P=1e9+7,N=1e6;
inline int In(){
    char c=getchar(); int x=0,ft=1;
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
    for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
    return x*ft;
}
int k[55],fac[N+5],inv[N+5],S[105][105],ans=0;
inline int power(int x,int r){
    int s=1,t=x;
    for(;r;r>>=1,t=1ll*t*t%P) if(r&1) s=1ll*s*t%P;
    return s;
}
inline int C(int m,int n){
    if(m<n) return 0;
    return 1ll*fac[m]*inv[n]%P*inv[m-n]%P;
}
inline int g(int T,int M,int R){
    int res=0;
    for(int i=0,d=1;i<=R;++i,d=P-d)
    res=(res+1ll*d*C(R,i)%P*C(T-i*(M+1)+R-1,R-1)%P)%P;
    return res;
}
int Calc(int Tx,int Ty,int Mx,int My,int R){
    int res=0;
    for(int i=0,d=(R&1?P-1:1);i<=R;++i,d=P-d)
    res=(res+1ll*d*C(R,i)%P*g(Tx,Mx,i)%P*g(Ty,My,i)%P)%P;
    return res;
}
int main(){
    int Tx=In(),Ty=In(),Mx=In(),My=In(),R=In();
    int G=In(),K=In(),lim=(Tx<Ty?Tx:Ty)/G;
    for(int i=1;i<=K;++i) k[i]=In(),k[i]/=G;
    sort(k+1,k+1+K); K=unique(k+1,k+1+K)-k-1;
    fac[0]=1;
    for(int i=1;i<=N;++i) fac[i]=1ll*fac[i-1]*i%P;
    inv[N]=power(fac[N],P-2);
    for(int i=N-1;~i;--i) inv[i]=1ll*inv[i+1]*(i+1)%P;
    S[0][0]=1;
    for(int i=0;i<R&&i<lim;++i)
    for(int j=0;j<=lim;++j) if(S[i][j])
    for(int t=1;t<=K;++t)
    S[i+1][j+k[t]]=(S[i+1][j+k[t]]+S[i][j])%P;
    for(int i=0,d=1;i<=R&&i<=lim;++i,d=P-d)
    for(int j=i;j<=lim;++j) if(S[i][j]){
        ans=(ans+1ll*d*S[i][j]%P*Calc(Tx-j*G,Ty-j*G,Mx,My,R-i)%P*C(R,i)%P)%P;
    }
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/pkh68/p/10549078.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值