Codeforces Round #630 (Div. 2) E - Height All the Same

我们可以让一个块+2,或让两个相邻的块都+1。

显然,如果初始状态与目标状态的奇偶性相同一定可行。

所以当n*m为奇数,初态奇偶状态是什么都可,(因为末态有奇数有偶数),这个直接快速幂即可

当n*m是偶数时,初始状态必须时偶数,因为末态只有偶数

然后就求出n*m个数,每个数取值L-R,和是偶数的方案:

第一种方法:线性dp+矩阵快速幂加速

dp[i][j]表示选了i个数,和为:偶数/奇数 的方案数,是个1e9的线性dp,用矩阵快速幂加速下即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,-1,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
*/
const int mod= 998244353;
ll qpow(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b/=2;
	}
	return ans;
}
int dp[M][2];
struct matrix
{
	ll c[2][2];
};
matrix operator *(const matrix &a,const matrix &b)
{
	matrix c={0};
	for(int i=0;i<=1;i++)
	for(int j=0;j<=1;j++)
	for(int k=0;k<=1;k++)
		c.c[i][j]=(c.c[i][j]+a.c[i][k]*b.c[k][j]%mod)%mod;
	return c;
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	ll n,m,L,R;
  	cin>>n>>m>>L>>R;
  	ll ss=qpow(R-L+1,n*m);
  	if(n*m%2==1)cout<<ss<<endl;
  	else 
  	{
  		ll tp=0;//奇数 
  		if((R-L)&1)tp=(R-L+1)/2;
  		else 
  		{
  			if(R&1)tp=(R-L)/2+1;
  			else tp=(R-L)/2;
		}
		ll tt=(R-L+1)-tp;//偶数 
		ll x=n*m;
		matrix a={tt,tp,tp,tt};
		matrix ans=matrix{1,0,0,1};
  		while(x)
  		{
  			if(x&1)ans=ans*a;
		    a=a*a;
  			x/=2;
		}
		cout<<ans.c[0][0]<<endl;
//		dp[0][0]=1;
//		for(int i=1;i<=n*m;i++)
//		dp[i][0]=dp[i-1][0]*tt%mod+dp[i-1][1]*tp%mod,
//		dp[i][1]=dp[i-1][0]*tp%mod+dp[i-1][1]*tt%mod;
//		cout<<dp[n*m][0]%mod<<endl;
	}
	return 0;
}

方法二:线性递推+分析

如果R-L+1是偶数,那么每个数取奇数偶数的可能相同,线性递推发现,奇数偶数的方案一定相同(不懂得可以观察下DP递推的过程)

如果R-L+1是奇数,且R是奇数:

设奇数a个,则偶数a-1个,

第一个数奇数有a种,偶数有b种

到第二个数时:

奇数有:a*b+(a-1)*a=a*a+a*b-a

偶数有:a*a+(a-1)*b=a*a+a*b-b

由于-a+b=-1,所以偶数项偶数比奇数多1

以此类推。

n个数填完,奇数的方案比偶数多1个。只需求出所有奇偶方案数,除以2再加1即可

如果R-L+1是偶数 同理:

设奇数a个,则偶数a+1个,

第一个数奇数有a种,偶数有b种

到第二个数时:

奇数有:a*b+(a+1)*a=a*a+a*b+a

偶数有:a*a+(a+1)*b=a*a+a*b+b

由于a-b=-1,所以偶数项偶数比奇数多1

以此类推。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,-1,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
*/
const int mod= 998244353;
ll qpow(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b/=2;
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	ll n,m,L,R;
  	cin>>n>>m>>L>>R;
  	ll ss=qpow(R-L+1,n*m);
  	if(n*m%2==1)cout<<ss<<endl;
  	else 
  	{
  		ll x=R-L+1;
  		ll inv=qpow(2,mod-2),ans;
  		if(x&1)ans=((ss+1)*((mod+1)/2))%mod;
  		else ans=((ss)*((mod+1)/2))%mod;
		  cout<<ans<<endl;
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值