HIT 校测 Round1 F - dp - 二项式定理 -
题意:求的个数,使得且中数码4的个数为4的倍数,
题解:
第一个条件可以转化为末两位为4的倍数。易知其中不含数码4的有18个,含1个数码4的有6个,2个有1个。
先考虑不含数码4的,剩下位如何处理(判掉n=1或2的)。显然答案就是
如何计算?联想到二项式定理。但是如何只保留4的倍数项?
这个trick P大夏令营出过原题,只不过当时是3的倍数项。3b1b也有个类似的视频,在讲生成函数的这一期
考虑
我现在只想保留0,4,8..项。考虑到可以利用模意义下的单位根来构造消去。即,跑一下发现,也恰好满足的条件。构造式子如下:
,即可得到上式,这块答案乘以18即可
再考虑含2个数码4的,答案就是,发现能和上面组成一个完整的偶数项。高中技巧w=1和-1代入相加除以2即可
在考虑含1个数码4的,即求思路还是消项,经过构造发现答案就是,最后乘以6即可
时间复杂度
#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:我,真是个笨蛋
设表示当前考虑到第位,4的个数模4为0/1/2/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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下