JZOJ 4268. 【五校联考8day2】数

题意

∑ i = 0 n ∑ j = 0 ⌊ i 2 ⌋ C i − 1 j \sum_{i=0}^n\sum_{j=0}^{\lfloor\frac{i}{2}\rfloor}C_{i-1}^j i=0nj=02iCi1j
n ≤ 1 0 10000 n≤10^{10000} n1010000

题解

对组合数的感知。
题目给组合数,希望我们能够正确理解其意义。
假设i固定了,那么 ∑ j = 0 ⌊ i 2 ⌋ C i − 1 j \sum_{j=0}^{\lfloor\frac{i}{2}\rfloor}C_{i-1}^j j=02iCi1j就代表一堆石子有i个,一次可以拿1个或2个。问将石子拿完的方案数。
显然是斐波那契数列。
最直接的想法:n很大,那么压位。斐波那契?矩乘。
下面是我的AC代码。

有一位大佬提出,n虽然很大,但是答案一定有循环节,且循环节不超过998244353+1。(显然)
所以,n只需要模998244354即可。
然后闪过。。。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 2005
#define mo 998244353
#define Mo 100000000
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--) 
using namespace std;
struct eq{
	int a[N];
	int l;
	void clear(){
		memset(a,0,sizeof(a));
	}
}f1;
struct mat{
	int i,j,a[2][2];
	void clear(){
		memset(a,0,sizeof(a));
	}
	void unit(){
		clear();
		fo(i,0,1)a[i][i]=1;
	}
}F1,F2,F3;
char s[10010];
int len,Len,i,j,k,n,m,ans;
int cnt,x2;
int Max(int x,int y){return x>y?x:y; }
eq plus(eq a,eq b){
	eq c;c.clear();
	int i,l=Max(a.l,b.l);
	fo(i,1,l){
		c.a[i]=a.a[i]+b.a[i];
		c.a[i+1]=c.a[i]/Mo;
		c.a[i]=c.a[i]%Mo;
	}
	c.l=l+1;
	while(!c.a[c.l]&&c.l>1)c.l--;
	return c;
}
eq divd(eq a,int b){
	eq c;c.clear();
	int i,l=a.l;
	LL ys=0;
	fd(i,l,1){
		ys=(1ll*ys*Mo+1ll*a.a[i]);
		c.a[i]=c.a[i]+(ys/b);
		ys=(ys-1ll*c.a[i]*b);
	}
	c.l=l;
	while(!c.a[c.l]&&c.l>1)c.l--;
	return c;
}
mat M_mul(mat a,mat b){
	mat c;c.clear();
	int i,j,k;
	fo(k,0,1)fo(i,0,1)if(a.a[i][k]){
		fo(j,0,1)c.a[i][j]=(1ll*c.a[i][j]+(1ll*a.a[i][k]*b.a[k][j])%mo)%mo;
	}
	return c;
}
int main(){
	scanf("%s\n",s+1);
	len=strlen(s+1);
	if(len==1&&s[1]=='0'){
		printf("1");
		return 0;
	}
	cnt=0;x2=0;
	for(i=len;i>0;i-=8){
		if(i<=8){
			x2=0;
			fo(j,1,i)x2=(x2<<3)+(x2<<1)+(s[j]^'0');
			f1.a[++Len]=x2;
			break;
		}
		x2=0;
		fo(j,i-7,i)x2=(x2<<3)+(x2<<1)+(s[j]^'0');
		f1.a[++Len]=x2;
	}
	f1.l=Len;
	F2.unit();
	F3.a[0][1]=F3.a[1][0]=F3.a[1][1]=1;
	while(!(f1.l==1&&f1.a[1]==0)){
		if(f1.a[1]&1)F2=M_mul(F2,F3);
		f1=divd(f1,2);
		F3=M_mul(F3,F3);
	}
	ans=(1ll*F2.a[0][0]+F2.a[0][1])%mo;
	ans=(1ll*ans*(1ll*F2.a[1][0]+F2.a[1][1]))%mo;
	printf("%d",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值