【Zhengrui2021冬休み地方選挙第2回研修1日目】トークン生成(組み合わせ数+ 2点)

説明

ここに画像の説明を挿入しますここに画像の説明を挿入します

解決


ここに画像の説明を挿入しますここに画像の説明を挿入します条件を満たすyydの数は、実際にはnnと同じです。n(タイトルの意味ではありません)は等差数列の式と密接に関連している
ため、特定の値を2つに分割できます
。最終的な回答の値の範囲は[、)[、)に拡大する必要があります。[ 、左側が閉じて右側が開いて
おり、2つの境界は差が最小の境界である必要があります1 11、次いで回答のバイナリ態様は、固定されており、
ここに画像の説明を挿入します
同じことに分割され、1
ここで、 1の特定の位置を組み合わせの数と関連付けることができます。これにより、少数の組み合わせを前処理して、クエリ時間の複雑さを軽減できます。

個人的にはまだ考えるのは難しいと思います!

コード

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAX 1e18
#define ll long long
#define mod 998244353
ll c[3005][3005];

ll qkpow1( ll x, ll y ) {
    
    
	ll ans = 1;
	while( y ) {
    
    
		if( y & 1 ) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}

ll qkpow2( ll x, ll y ) {
    
    
	ll ans = 1;
	while( y ) {
    
    
		if( y & 1 ) ans *= x;
		x *= x;
		y >>= 1;
	}
	return ans;
}

ll calc( ll n ) {
    
    
	return n * ( n + 1 ) >> 1;
}

ll C( ll n, ll m ) {
    
    
	if( n <= 3000 && m <= 3000 ) return c[n][m];
	if( m > n - m ) m = n - m;
	if( n >= 1e7 && m >= 3 ) return MAX + 1;
	__int128 d1 = 1, d2 = 1;
	for( int i = 1;i <= m;i ++ ) d1 *= i;
	for( int i = n - m + 1;i <= n;i ++ ) {
    
    
		d2 *= i;
		if( d2 > d1 * MAX ) return MAX + 1;
	}
	return d2 / d1;
}

void solve( ll n, ll k, ll ans ) {
    
    
	ll t = 0, tmp;
	while( k > ( tmp = C( n, t ) ) ) k -= tmp, t ++;
	int last = n;
	for( ll i = 1;i <= t;i ++ ) {
    
    
		ll l = 1, r = last, pos;
		while( l <= r ) {
    
    
			ll mid = ( l + r ) >> 1;
			if( C( mid - 1, t - i + 1 ) < k ) pos = mid, l = mid + 1;
			else r = mid - 1;
		}
		last = pos - 1;
		k -= C( pos - 1, t - i + 1 );
		ans = ( ans + qkpow1( 2, pos - 1 ) ) % mod;
	}
	printf( "%lld\n", ans );
}

void init( int n = 3000 ) {
    
    
	for( int i = 0;i <= n;i ++ )
		for( int j = 1;j <= i;j ++ )
			c[i][j] = MAX + 1;
	for( int i = 0;i <= n;i ++ ) {
    
    
		c[i][0] = 1;
		for( int j = 1;j <= i;j ++ )
			c[i][j] = min( c[i][j], c[i - 1][j] + c[i - 1][j - 1] );
	}
}

int main() {
    
    
	init();
	int T; ll n, k;
	scanf( "%d", &T );
	while( T -- ) {
    
    
		scanf( "%lld %lld", &n, &k );
		ll t = sqrt( n << 1 );
		while( calc( t ) > n ) t --;
		ll t1 = n - calc( t ), t2 = t - t1;
		if( ! t1 ) {
    
    
			if( k == 1 ) printf( "%lld\n", ( qkpow1( 2, t ) - 1 + mod ) % mod );
			else printf( "-1\n" );
		}
		else if( t2 < 60 && qkpow2( 2, t2 ) < k )
			printf( "-1\n" );
		else solve( t2, k, ( qkpow1( 2, t1 ) - 1 + mod ) % mod * qkpow1( 2, t2 + 1 ) % mod );
	}
	return 0;
}

おすすめ

転載: blog.csdn.net/Emm_Titan/article/details/113893715