zoukankan      html  css  js  c++  java
  • [THUPC2019]过河卒二

    题目

    好难啊,不会啊;啊,(kleq 20),那就直接容斥吧;

    我们硬点一些控制点,之后使我们必须经过这些被硬点的点;由于我们只能往右上走,所以经过这些被钦定的点的顺序是一定的,我们只需要考虑相邻两点之间的路径数最后乘起来就好;

    于是我们需要推一个(f(n,m))表示从((0,0))走到((n,m))的路径数;

    如果没有那个斜向上走直接组合数即可,有了这个斜向上走我们就枚举斜向上走了多少次,于是

    [f(n,m)=sum_{k=0}^minom{m+n-2k}{m-k}inom{m+n-k}{m+n-2k} ]

    (inom{m+n-2k}{m-k})就是走到了((k,k))这个位置之后我们只往右往上走,(inom{m+n-k}{m+n-2k})就是把(k)个斜向上走插到(n+m-2k)步里去;

    这里的模数特别小,于是我们Lucas一下,就可以(O(mlog_p n))计算(f(n,m)),由于(f)之后在两两关键点之间用到,于是直接(O(mk^2log_p n))预处理好即可;

    最后我们还需要考虑从最后一个钦定点走出边界的情况,设(g(n,m))表示起点为((0,0)),矩形右上角为((n,m))走出边界的方案数;

    我们规定从上走出边界的点最后都要走到((n,m+1)),从右走出边界的都走到((n+1,m));特殊的,还有在((n,m))位置斜向上走出边界的,于是(g(n,m)=f(n+1,m)+f(n,m+1)+f(n,m))

    代码

    #include<bits/stdc++.h>
    #define re register
    const int mod=59393;
    struct node{int x,y;}p[25];
    inline int dqm(int x){return x<0?x+mod:x;}
    inline int qm(int x){return x>=mod?x-mod:x;}
    int fac[mod],ifac[mod],inv[mod],ans[25][25],lst[25],fst[25],id[25],n,m,k;
    inline int cmp(int a,int b){return p[a].x==p[b].x?p[a].y<p[b].y:p[a].x<p[b].x;}
    inline int C(int n,int m) {
    	return m>n?0:1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
    }
    inline int Lucas(int n,int m) {
    	if(m>n)return 0;if(!m)return 1;
    	return 1ll*Lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod;
    }
    inline int f(int n,int m) {
    	if(m<0||n<0)return 0;
    	if(m>n)std::swap(n,m);int ans=0;
    	for(re int i=0;i<=m;++i)ans=qm(ans+1ll*Lucas(m+n-i,n)*Lucas(n,n-i)%mod);
    	return ans; 
    }
    inline int g(int n,int m) {return qm(qm(f(n+1,m)+f(n,m+1))+f(n,m));}
    inline int calc(int state) {
    	int cnt=0;
    	for(re int i=0;i<k;i++)if(state>>i&1)id[++cnt]=i+1;
    	id[++cnt]=0;int nw=(cnt&1?1:mod-1);
    	std::sort(id+1,id+cnt+1,cmp);
    	for(re int i=1;i<cnt;i++) nw=1ll*nw*ans[id[i]][id[i+1]]%mod;
    	return 1ll*nw*lst[id[cnt]]%mod;
    }
    int main() {
    	fac[0]=ifac[0]=inv[1]=1;
    	for(re int i=1;i<mod;++i)fac[i]=1ll*fac[i-1]*i%mod;
    	for(re int i=2;i<mod;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    	for(re int i=1;i<mod;++i)ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
    	scanf("%d%d%d",&n,&m,&k);int tot=0;p[0].x=1,p[0].y=1;
    	for(re int i=1;i<=k;i++)scanf("%d%d",&p[i].x,&p[i].y);
    	for(re int i=0;i<=k;i++)for(re int j=0;j<=k;j++)
    		if(i!=j)ans[i][j]=f(p[j].x-p[i].x,p[j].y-p[i].y);
    	for(re int i=0;i<=k;i++) lst[i]=g(n-p[i].x,m-p[i].y);
    	for(re int i=0;i<(1<<k);++i) tot=qm(tot+calc(i));
    	printf("%d
    ",tot);
    }
    
  • 相关阅读:
    C++ Compress Floder
    C语言: 两个int变量相除,结果保留两位小数
    过滤Windows文件名中的非法字符
    判断两个vector是否相等
    顶级操盘手是怎样准确把握入场时机的
    短线黑马选股绝技
    短线黑马选股绝技 一
    每日一招:短线炒股实用技巧
    高抛低吸T+0操作要领(目前行情短线炒作的必备技能)
    如何买开盘即涨停的个股
  • 原文地址:https://www.cnblogs.com/asuldb/p/12189043.html
Copyright © 2011-2022 走看看