牛客NOIP2021提高组OI赛前模拟赛第一场T3——与巨(数学)

description

【题目描述】
定义无穷序列 f : f 1 = 1 , f n = f n − 1 ∗ 2 + 1 f:f_1=1,f_n=f_{n-1}*2+1 f:f1=1,fn=fn12+1

定义函数 G ( x ) = min ⁡ f i ≥ x ( f i ) G(x)=\min_{f_i\ge x}(f_i) G(x)=minfix(fi)

定义 d p c , 0 = 0 , d p c , i = max ⁡ ( d p c , i − 1 , [ ( ( i ∗ c ) & G ( i ) ) = i ] ∗ i ) dp_{c,0}=0,dp_{c,i}=\max(dp_{c,i-1},\big[\big((i*c)\&G(i)\big)=i\big]*i) dpc,0=0,dpc,i=max(dpc,i1,[((ic)&G(i))=i]i)

∑ i = 0 n d p c , i ( m o d 998244353 ) \sum_{i=0}^ndp_{c,i}\pmod {998244353} i=0ndpc,i(mod998244353)

【输入格式】
第一行输入一个整数T,表示测试用例的组数。
每组测试用例输入一行包含两个整数n, c。
其中𝐧以二进制形式表示,且𝐧不含有前导 0。
【输出格式】
对于每组测试用例输出一行一个整数表示答案。
【样例 1 输入】

5
1001 1
11111 1
101010111101 8999
10100101111010101 799
10010010 233

【样例 1 输出】

45
496
2835797
707482963
9940  

【数据范围】

1 ≤ T ≤ 10 ; 1 ≤ ∣ n ∣ ≤ 1 0 7 , 1 ≤ c ≤ 1 0 18 , ∑ i = 1 T ∣ n ∣ ≤ 1 0 7 1\le T\le 10;1\le |n|\le 10^7,1\le c\le 10^{18},\sum_{i=1}^T|n|\le 10^7 1T10;1n107,1c1018,i=1Tn107

solution

观察到 f i f_i fi的生成递推式,最后结果一定形如000..00011...111

G ( i ) = 2 t + 1 − 1 G(i)=2^{t+1}-1 G(i)=2t+11 G ( i ) G(i) G(i)必然是这种形式, t t t为二进制最高位

x & G ( i ) x\&G(i) x&G(i)相当于 x  mod  2 t + 1 x\ \text{mod}\ 2^{t+1} x mod 2t+1

则,有 ( i ∗ c ) & G ( i ) = i ⇔ ( i ∗ c )  mod  2 t + 1 = i ⇔ i ∗ ( c − 1 )  mod  2 t + 1 = 0 (i*c)\&G(i)=i\Leftrightarrow (i*c)\ \text{mod}\ 2^{t+1}=i\Leftrightarrow i*(c-1)\ \text{mod}\ 2^{t+1}=0 (ic)&G(i)=i(ic) mod 2t+1=ii(c1) mod 2t+1=0

2 p ∗ q = c − 1 2^p*q=c-1 2pq=c1来重新表示,则 i i i必须含有因子 2 t + 1 − p 2^{t+1-p} 2t+1p

m = ∣ n ∣ m=|n| m=n,可以枚举 t t t

  • t < m − 1 t<m-1 t<m1

    [ 2 t , 2 t + 1 − 1 ] [2^t,2^{t+1}-1] [2t,2t+11]内的所有数都在 n n n之内,其最高位均为 t t t

    g = t + 1 − p g=t+1-p g=t+1p,那么 i i i含有因子 2 g 2^g 2g意味着 i i i的低 g g g位全为 0 0 0

    这样的数为 2 t , 2 t + 2 g , . . . , 2 t + x ∗ 2 g 2^t,2^t+2^g,...,2^t+x*2^g 2t,2t+2g,...,2t+x2g 2 t + ( x + 1 ) 2 g = 2 t + 1 2^t+(x+1)2^g=2^{t+1} 2t+(x+1)2g=2t+1

    这是一个 x + 1 x+1 x+1项的等差数列,利用公式容易求得

    同时每个数都会对最终答案贡献 2 g 2^g 2g次【对于 G ( i ) = 2 a G(i)=2^a G(i)=2a,那么 G ( i + k , k ∈ [ 1 , 2 a ] ) = 2 a + 1 G(i+k,k\in[1,2^a])=2^{a+1} G(i+k,k[1,2a])=2a+1 2 a + 1 2^{a+1} 2a+1都会贡献,一共是 2 a 2^a 2a

  • t = m − 1 t=m-1 t=m1,由于 2 t + 1 > n 2^{t+1}>n 2t+1>n,但需要满足 2 t + x ∗ 2 t ≤ n ⇒ x = ⌊ n 2 g ⌋ − 2 t − g 2^t+x*2^t\le n\Rightarrow x=\lfloor\frac{n}{2^g}\rfloor-2^{t-g} 2t+x2tnx=2gn2tg

    对这个 x + 1 x+1 x+1项的等差数列同样方法计算贡献后,最后一项被计算了 2 g 2^g 2g次,计数范围为 [ 2 t + x ∗ 2 g , 2 t + ( x + 1 ) ∗ 2 g − 1 ] [2^t+x*2^g,2^t+(x+1)*2^g-1] [2t+x2g,2t+(x+1)2g1]

    此时多计数了 2 t + ( x + 1 ) ∗ 2 g − 1 − n 2^t+(x+1)*2^g-1-n 2t+(x+1)2g1n次,减去多的贡献即可

code

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define int long long
#define mod 998244353
#define maxn 10000005
int mi[maxn];
char s[maxn];
const int inv2 = ( mod + 1 ) >> 1;

int calc( int a, int d, int n ) {
	return ( a * n % mod + n * ( n - 1 ) % mod * d % mod * inv2 % mod ) % mod;
}

signed main() {
	freopen( "and.in", "r", stdin );
	freopen( "and.out", "w", stdout );
	mi[0] = 1;
	for( int i = 1;i < maxn;i ++ ) mi[i] = ( mi[i - 1] << 1 ) % mod;
	int T, n, c;
	scanf( "%lld", &T );
	while( T -- ) {
		scanf( "%s %lld", s + 1, &c );
		n = strlen( s + 1 );
		c --;
		int ans = 0;
		if( ! c ) {
			for( int i = 1;i <= n;i ++ )
				ans = ( ( ans << 1 ) + ( s[i] ^ 48 ) ) % mod;
			printf( "%lld\n", ans * ( ans + 1 ) % mod * inv2 % mod );
		}
		else {
			if( c & 1 ) { printf( "0\n" ); continue; }
			else {
				int p = 0;
				while( ! ( c & 1 ) ) c >>= 1, p ++;
				for( int t = 0;t < n;t ++ ) {
					int g = max( 0ll, t + 1 - p );
					if( t < n - 1 )
						ans = ( ans + mi[g] * calc( mi[t], mi[g], mi[t + 1 - g] - mi[t - g] ) ) % mod;
					else {
						int k = 0;
						for( int i = 1;i <= n - g;i ++ ) k = ( ( k << 1 ) + ( s[i] ^ 48 ) ) % mod;//n/2^g下取整 
						int x = ( k - mi[t - g] ) % mod; 
						ans = ( ans + mi[g] * calc( mi[t], mi[g], x + 1 ) ) % mod;//x=k-2^{t-g}=k-mi[t-g] x+1项 
						int lst = ( mi[t] + x * mi[g] ) % mod;//计算末项 2^t+x*2^g
						int l = 0;
						int r = ( lst + mi[g] - 1 ) % mod;//末项的下一项 2^t+(x+1)*2^g  还有一个-1 
						for( int i = 1;i <= n;i ++ ) l = ( ( l << 1 ) + ( s[i] ^ 48 ) ) % mod; //-n 
						ans = ( ans - ( r - l ) * lst ) % mod;
					}
				}
			}
			printf( "%lld\n", ( ans + mod ) % mod );
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值