zoukankan      html  css  js  c++  java
  • 2019 ICPC Asia-East Continent Final 部分题题解

    题面

    Problem B. Black and White

    题目大意:有一个(n) ( imes) (m)的网格,网格之中的格子有黑白两种颜色。
    被(0,0),(0,1),(1,0),(1,1)包围的格子是白色,一个格子周围的四个格子的颜色与其都不相同。
    现要从(0,0)走到(n,m),每次只能向上或向右走。
    给定(k),求满足条件的路径数量,使得这条路径左边的白格子数-黑格子数=(k)
    多组数据。
    (n,m,|w| leq 1e5)(T leq 100)
    题解:
    设当前白格子数-黑格子数为(w)
    发现每次如果只走一步,我们没法判断当前的(w)的变化量。那么考虑走两步。
    如果每次都走两步的话,那无非就四种情况:
    1.( ightarrow ightarrow)
    2.(uparrow uparrow)
    3.(uparrow ightarrow)
    4.( ightarrow uparrow)
    前两种情况对(w)无影响,而3,4这两种情况可能会对(w)造成影响。
    考虑枚举3,4操作的总次数(p),以及4操作的次数(q)
    通过打表,可以发现一个式子:

    [lfloor frac{p+1}{2} floor - q = k ]

    由此,我们只需要枚举(p),就能得到合法的(q)了。
    现在考虑统计方案数。
    (x=frac{n+m}{2}),那么相当于在(x)次操作中任选(p)次,又在(p)次中任选(q)个。
    然后情况1,2的组合,就是在(x-p)次中选(frac{n-p}{2})次。也就是三个组合数。
    注意所有这些要除以2的地方都要求是偶数才有意义。
    如果(n+m)是奇数,讨论一下第一步怎么走,然后正常算两次就好了。
    时间复杂度:(O(Tn))
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline int
    #define STS system("pause")
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    const int Mod=998244353,N=100010;
    int T,fac[101000],inv[101000];
    I add(int &x,int y){(x+=y)>=Mod?x-=Mod:0;}
    IN Plus(int x,int y){(x+=y)>=Mod?x-=Mod:0;return x;}
    IN Pow(int x,int y=Mod-2){
    	re res=1;
    	while(y){
    		if(y&1)res=(ll)res*x%Mod;
    		x=(ll)x*x%Mod;
    		y>>=1;
    	}
    	return res;
    }
    IN C(int x,int y){if(x<y)return 0;return (ll)fac[x]*inv[y]%Mod*inv[x-y]%Mod;}
    I init(){
    	fac[0]=1;
    	F(i,1,N)fac[i]=(ll)fac[i-1]*i%Mod;
    	inv[N]=Pow(fac[N]);
    	FOR(i,N-1,0)inv[i]=(ll)inv[i+1]*(i+1)%Mod;
    }
    IN solve(int n,int m,int k){
    	if(!n||!m)return k==0?1:0;
    	re x=(n+m)>>1,ans=0;
    //	cout<<n<<" "<<m<<" "<<k<<" "<<x<<endl;
    	for(re p=0,q;p<=x;p++){
    		q=((p+1)>>1)-k;
    		if(q<0||q>p||((n-p)&1)||p>n)continue;
    //		cout<<"!"<<p<<" "<<q<<endl;
    		add(ans,(ll)C(x,p)*C(p,q)%Mod*C(x-p,(n-p)>>1)%Mod);
    	}
    	return ans;
    }
    int n,m,k;
    int main(){
    	read(T);init();
    	while(T--){
    		read(n);read(m);read(k);
    		if(!((n+m)&1))printf("%d
    ",solve(n,m,k));
    		else printf("%d
    ",Plus(solve(n-1,m,-k),solve(n,m-1,(n&1)-k)));
    	}
    	return 0;
    }
    /*
    5
    1 1 0
    1 1 -1
    2 2 1
    2 2 0
    4 4 1
    */
    
  • 相关阅读:
    复数加法
    通过函数来实现复数相加
    声明一个类模板
    友元成员函数的简单应用
    将普通函数声明为友元函数
    引用静态数据成员
    对象的赋值
    对象的常引用
    有关对象指针的使用方法
    对象数组的使用方法
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/13210542.html
Copyright © 2011-2022 走看看