Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2) H. Asterism Stream(期望dp+矩阵快速幂/生成函数)

题目

有一个数x,x初始为1,等概率地执行以下两种操作:

1. 将x加1

2. 将x乘2

求x>=n(n<=1e18)时的期望操作次数,

答案(有理分数)对998244353取模

思路来源

dls&jiangly做法

90cf4d7b0e134caa804dddc0642f656a.png

题解

题解1. 矩阵快速幂的比较难想,没遇到过矩阵快速幂维护x/2的,感觉也是一个典

题解2. 有生成函数的比较难推,即使推出来之后也不会算,感觉又是一个典

以及,这个题知道做法了之后,还是很难写出与之对应的代码,很难和神仙代码对应起来…

题解1(期望dp+矩阵快速幂)

2%5Ek%29%5C%5C1%20%5Cend%7Bbmatrix%7D

维护这么一个矩阵,考虑转移矩阵怎么写,

实际大概2的60次方到1e18,维护一个60*60的矩阵,

为了说明与ctz(二进制尾0的个数/最低位1的位置)的关系,举俩小点的例子

eq?%5Cbegin%7Bbmatrix%7D%20f%283%29%5C%5C%20f%281%29%5C%5Cf%280%29%5C%5C%201%20%5Cend%7Bbmatrix%7D%20%3D%20%5Cbegin%7Bbmatrix%7D%200.5%20%26%200.5%20%26%200%20%26%201%5C%5C%200%20%26%201%20%26%200%20%26%200%20%5C%5C%200%20%26%200%20%26%201%20%26%200%20%5C%5C%200%20%26%200%20%26%200%20%26%201%5Cend%7Bbmatrix%7D%20%5Cbegin%7Bbmatrix%7D%20f%282%29%5C%5C%20f%281%29%5C%5Cf%280%29%5C%5C%201%20%5Cend%7Bbmatrix%7D

eq?%5Cbegin%7Bbmatrix%7D%20f%282%29%5C%5C%20f%281%29%5C%5Cf%280%29%5C%5C%201%20%5Cend%7Bbmatrix%7D%20%3D%20%5Cbegin%7Bbmatrix%7D%200.5%20%26%200.25%20%26%200.25%20%26%201%5C%5C%200%20%26%200.5%20%26%200.5%20%26%201%20%5C%5C%200%20%26%200%20%26%201%20%26%200%20%5C%5C%200%20%26%200%20%26%200%20%26%201%5Cend%7Bbmatrix%7D%20%5Cbegin%7Bbmatrix%7D%20f%281%29%5C%5C%20f%280%29%5C%5Cf%280%29%5C%5C%201%20%5Cend%7Bbmatrix%7D

你可能会说,f(2)的时候,0.25f(0)+0.25f(0)和0.5f(0)不是一样的么...

但是,n稍微大一点的时候,n-1的第二行和第三行就不一样了,这种做法具有普适性

注意f(3)的时候转移矩阵只需要用第一行,而f(2)的时候需要用前两行

 

需要用几行,即n和n-1相比,除以2的k次方什么时候不变,

这只和ctz有关,即n的二进制尾0的个数/最低位1的位置

换言之,转移矩阵只有60种,

矩阵快速幂,以倍增的思想,求出处理出前n-1个转移矩阵积,

再乘上原始f[0]的向量,得到的向量的第一行即为答案

题解2

待补

代码1(dls)

直接复制的dls的代码,自己写不出来这么简洁,重在理解吧只能说…

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef basic_string<int> BI;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
mt19937 mrand(random_device{}()); 
const ll mod=998244353;
int rnd(int x) { return mrand() % x;}
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
// head

ll inv2=(mod+1)/2;
const int M=61;

typedef ll matrix[M+1][M+1];

matrix f[66],g[66],h[66],tmp1;
ll b[66],tmp2[66];

void mul(matrix a,matrix b,matrix c) {
	rep(i,0,M+1) rep(j,0,M+1) {
		c[i][j]=0;
		rep(k,0,M+1) c[i][j]=(c[i][j]+a[i][k]*b[k][j])%mod;
	}
}
void mulx(matrix a,ll *b,ll *c) {
	rep(i,0,M+1) {
		c[i]=0;
		rep(j,0,M+1) c[i]=(c[i]+a[i][j]*b[j])%mod;
	}
}

int main() {
	for (int i=0;i<M-1;i++) {
		for (int j=i+1;j<=M;j++) {
			f[i][j][j]=1;
		}
		for (int j=i;j>=0;j--) {
			f[i][j][M]=1;
			rep(k,0,M+1) f[i][j][k]=(f[i][j][k]+f[i][j+1][k]*inv2)%mod;
			f[i][j][j]=(f[i][j][j]+inv2)%mod;
		}
	}
	rep(i,0,M+1) rep(j,0,M+1) {
		g[0][i][j]=f[0][i][j];
		h[0][i][j]=f[0][i][j];
	}
	rep(i,1,M) {
		mul(f[i],g[i-1],h[i]);
		mul(g[i-1],h[i],g[i]);
	}
	int _;
	for (scanf("%d",&_);_;_--) {
		ll n;
		scanf("%lld",&n);
		--n;
		rep(i,0,M+1) b[i]=(i==M)?1:0;
		per(i,0,M) if (n&(1ll<<i)) {
			mulx(h[i],b,tmp2);
			rep(j,0,M+1) b[j]=tmp2[j];
		}
		printf("%lld\n",b[0]);
	}
}

代码2(jiangly)

待补

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值