HIT 校测 Round1 F - dp - 二项式定理 -

题意:求x[0,10n)的个数,使得4|xx中数码4的个数为4的倍数,n1016

题解:
第一个条件可以转化为末两位为4的倍数。易知其中不含数码4的有18个,含1个数码4的有6个,2个有1个。
先考虑不含数码4的,剩下t=n2位如何处理(判掉n=1或2的)。显然答案就是Ct09t+Ct49t4+...
如何计算?联想到二项式定理。但是如何只保留4的倍数项?
这个trick P大夏令营出过原题,只不过当时是3的倍数项。3b1b也有个类似的视频,在讲生成函数的这一期
考虑(9+w)t=Ct09t+Ct19t1w+Ct29t2w2+Ct39t3w3+...
我现在只想保留0,4,8..项。考虑到可以利用模意义下的单位根w+w2+w3+w4=0来构造消去。即w4=1(mod998244353),跑一下发现w=86583718,也恰好满足w+w2+w3+w4=0的条件。构造式子如下:
14((9+w)t+(9+w2)t+(9+w3)t+(9+w4)t),即可得到上式,这块答案乘以18即可

再考虑含2个数码4的,答案就是Ct29t2+...,发现能和上面组成一个完整的偶数项。高中技巧w=1和-1代入相加除以2即可

在考虑含1个数码4的,即求Ct39t3+...思路还是消项,经过构造发现答案就是14(w(9+w)t+w2(9+w2)t+w3(9+w3)t+w4(9+w4)t),最后乘以6即可

时间复杂度O(logn)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#define mpr make_pair
#define debug() cerr<<"Madoka\n"

using namespace std;

typedef long long LL;
int mod=998244353;
#define int LL
// 18 0; 6 1; 1 2

int pw(int x,int y){
	if(!y)return 1;if(y==1)return x;
	int mid=pw(x,y>>1);
	if(y&1)return 1ll*mid*mid%mod*x%mod;
	return 1ll*mid*mid%mod;
}

int pp = 86583718,ppp,pppp,ppppp;

void solve(){
	int n;scanf("%lld",&n);
	if(n == 1)puts("2");
	else if(n == 2)puts("18");
	else{
		int t1=pw(9+pp,n-2),t2=pw(9+ppp,n-2),t3=pw(9+pppp,n-2),t4=pw(9+ppppp,n-2),inv=pw(4,mod-2);
		int ans1 = (((t1+t2)%mod+(t3+t4)%mod)%mod) * inv%mod;
		int ans2 = (pw(10,n-2) + pw(8,n-2))%mod*pw(2,mod-2) - ans1;
		ans2 = (mod+ans2)%mod;
		int ans3 = ((pp*t1%mod+ppp*t2%mod)%mod + (pppp*t3%mod + ppppp*t4%mod)%mod)%mod*inv%mod;
		printf("%lld\n",((ans1*18ll%mod + ans2)%mod + ans3*6ll%mod)%mod);
	}
}

signed main(){
	ppp=1ll*pp*pp%mod, pppp = 1ll*ppp*pp%mod, ppppp=1ll*pppp*pp%mod;
	int te;scanf("%lld",&te);
	while(te--)solve();
	
	return 0;
}

标算写的矩阵快速幂优化dp,我应该是唯一写了这种做法的人。
UPD:我,真是个笨蛋
dp[i][0/1/2/3]表示当前考虑到第i位,4的个数模4为0/1/2/3
转移:dp[i][0]=dp[i1][0]9+dp[i1][3],其余同理。
发现能写成矩阵快速幂的形式,一刀秒了

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, mod = 998244353;

struct mat{
	int a[6][6];
	mat(){memset(a,0,sizeof a);}
};

mat mul(mat a,mat b){
	mat c;
	for(int i=1;i<=4;i++)
		for(int j=1;j<=4;j++)
			for(int k=1;k<=4;k++)
				(c.a[i][j] += 1ll*a.a[i][k]*b.a[k][j]%mod) %= mod;
	return c;
}

void solve(){
	LL n;scanf("%lld",&n);
	if(n <= 2){puts(n == 1 ? "2" : "18");return ;}
	mat base,res;
	base.a[1][1] = base.a[2][2] = base.a[3][3] = base.a[4][4] = 9;
	base.a[1][4] = base.a[2][1] = base.a[3][2] = base.a[4][3] = 1;
	res.a[1][1] = 1, res.a[2][1] = res.a[3][1] = res.a[4][1] = 0;
	LL m = n-2;
	while(m){
		if(m&1)res = mul(base,res);
		base = mul(base,base);
		m >>= 1;
	}
	printf("%lld\n",(((res.a[1][1] * 18ll%mod + res.a[4][1] * 6ll % mod)%mod + res.a[3][1]%mod)%mod));
}

signed main(){
	int te;scanf("%d",&te);
	while(te --)solve();

	return 0;
}


posted @   SkyRainWind  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示