[2021.4.7多校省选模拟33]A,B,C

文章目录

考试复盘

今天的题其实蛮温柔的

考完试预估分 160 160 160,好家伙到手的只有 1 4 \frac{1}{4} 41

第一题是原题,做过的,虽然忘记怎么做了。。。⊙︿⊙

但是因为本身较简单,考场上也想到了正解

但是因为我的 S B SB SB二分 m i d mid mid附近点的取舍可能导致了答案位置的偏差 1 1 1

(╥╯^╰╥)哭晕在厕所了谢谢

第二题感觉就是 D P DP DP转移,联想到了正睿的一道划段合并 D P DP DP

但是 p a s s pass pass了,发现自己需要将 i , i + 1 i,i+1 i,i+1 i , i − 1 i,i-1 i,i1是否相邻的信息拿到

自己的 D P DP DP转移不动

反正就是没想到再拿一个 D P DP DP,两个相互转移

打了 30 30 30的暴力表,结果又出现了经典菜谱——没输入完就直接 r e t u r n   0 return\ 0 return 0

丢了

第三题扑脸而来的 N T T NTT NTT味道,但是不会

只能写最原始的 D P DP DP暴力打表,结果忘记调用打表函数,结果全是 0 0 0

Y(>_<、)Y

总结:

今天考试的题目比较简单,所以暴露出了很多细节/策略问题,如果在省选场上出现那简直就是死亡名单

1.对细节偏差非常讲究的题目(T1)一定要构造数据想办法测一测那个临界附近

2.如果暴力分能打表,那么最好先写了后放在后面自己跑,继续往下做。今天是最后十几分钟打的表,时间就很紧张。如果一开始看到就直接打表肯定是不会这么慌张的(T3)

3.对于自己不喜欢写对拍这件事,真的要在后面每一场比赛中强迫自己写——不一定要拍正解,至少保证自己能拿到的分一定不能丢!(T2如果写对拍,肯定发现自己直接结束程序的bug)

省选在即,把握细节,优化策略,是最后的王牌!

A

考虑枚举作为中位数的妹子

然后两边一定是选一段等长的区间

设选的中位数在mid处

二分两边选多少数,设选了x个

比较这 2 x + 1 2x+1 2x+1个数的平均数和第 m i d − x mid-x midx n − x + 1 n-x+1 nx+1个数的平均数

如果前者大,说明 x x x应该变小,否则 x x x变大

#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 200005
int n;
double a[maxn], sum[maxn];

double calc( int pos, int x ) {
	return ( sum[pos] - sum[pos - x - 1] + sum[n] - sum[n - x] ) / ( x << 1 | 1 );
}

int main() {
	scanf( "%d", &n );
	for( int i = 1;i <= n;i ++ )
		scanf( "%lf", &a[i] );
	sort( a + 1, a + n + 1 );
	for( int i = 1;i <= n;i ++ )
		sum[i] = sum[i - 1] + a[i];
	double ret = 0;
	for( int i = 1;i <= n;i ++ ) {
		int l = 0, r = min( i - 1, n - i ), x = 0;
		while( l < r ) {
			int mid = ( l + r ) >> 1;
			if( calc( i, mid ) < calc( i, mid + 1 ) )
				l = mid + 1;
			else
				r = mid;
		}
		ret = max( ret, calc( i, l ) - a[i] );
	}
	printf( "%.4f\n", ret );
	return 0;
}

B

设计状态 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个数的排列且包含 j j j对非法相邻数对的方案数,当我们放入 i + 1 i+1 i+1这个数的时候发生3种状况:

  • 打破一对非法相邻数对

  • 增加一对非法相邻数对

  • 不变。

增加的情况就发生在 i i i i + 1 i+1 i+1相邻的情况

所以我们还需要知道 i i i是否处于某个非法相邻数对。

因此重新设计状态

f [ i ] [ j ] f[i][j] f[i][j]:前i个数的排列且包含j对非法相邻数对, i i i存在于非法相邻数对中的方案数

g [ i ] [ j ] g[i][j] g[i][j] i i i不存在于非法相邻数对中的方案数

转移就是考虑 i + 1 i+1 i+1的放置方法

f [ i ] [ j ] f[i][j] f[i][j] 的放置方法有4种:

  • 与i相邻且增加一对非法相邻数对,只会是 [ i − 1 , i , i + 1 ] , 1 [i-1,i,i+1], 1 [i1,i,i+1],1种方法

    f [ i + 1 ] [ j + 1 ] f[i+1][j+1] f[i+1][j+1]

  • i i i相邻且不改变非法相邻数对数,只会是 [ i − 1 , i + 1 , i ] , 1 [i-1,i+1,i], 1 [i1,i+1,i],1种方法

    f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j]

  • 不与 i i i相邻且减少一对非法相邻数对,有 j − 1 j-1 j1种方法

    g [ i + 1 ] [ j − 1 ] g[i+1][j-1] g[i+1][j1]

  • 不与i相邻且不改变非法相邻数对数,有 i − j i-j ij种方法

    g [ i + 1 ] [ j ] g[i+1][j] g[i+1][j]

g [ i ] [ j ] g[i][j] g[i][j] 3 3 3种情况

  • i i i相邻且增加一对非法相邻数对,可能是 [ i + 1 , i ] [i+1,i] [i+1,i] [ i , i + 1 ] , 2 [i,i+1],2 [i,i+1]2种方法

    f [ i + 1 ] [ j + 1 ] f[i+1][j+1] f[i+1][j+1]

  • 不与 i i i相邻且减少一对非法相邻数对,有 j j j种摆放方法

    g [ i + 1 ] [ j − 1 ] g[i+1][j-1] g[i+1][j1]

  • 不与 i i i相邻且不改变非法相邻数对数,有 i − j − 1 i-j-1 ij1种摆放方法

    g [ i + 1 ] [ j ] g[i+1][j] g[i+1][j]

最终的答案就是 g [ N ] [ 0 ] g[N][0] g[N][0]

#include <cstdio>
#include <cstring>
#define int long long
#define maxn 1005
int n, mod;
int f[maxn][maxn], g[maxn][maxn];

signed main() {
	while( ~ scanf( "%lld %lld", &n, &mod ) ) {
		memset( f, 0, sizeof( f ) );
		memset( g, 0, sizeof( g ) );
		g[1][0] = 1;
		for( int i = 1;i < n;i ++ )
		for( int j = 0;j < i;j ++ ) {
			f[i + 1][j + 1] = ( f[i + 1][j + 1] + f[i][j] ) % mod;
			f[i + 1][j] = ( f[i + 1][j] + f[i][j] ) % mod;
			if( j ) g[i + 1][j - 1] = ( g[i + 1][j - 1] + f[i][j] * ( j - 1 ) ) % mod;
			g[i + 1][j] = ( g[i + 1][j] + f[i][j] * ( i - j ) ) % mod;
			f[i + 1][j + 1] = ( f[i + 1][j + 1] + g[i][j] * 2 ) % mod;
			if( j ) g[i + 1][j - 1] = ( g[i + 1][j - 1] + g[i][j] * j ) % mod;
			g[i + 1][j] = ( g[i + 1][j] + g[i][j] * ( i - j - 1 ) ) % mod; 
		}
		printf( "%lld\n", ( g[n][0] + mod ) % mod );
	}
	return 0;
}

C

N = 3 × 7 × 11 × 47 = 10857 N=3\times 7\times 11\times 47 =10857 N=3×7×11×47=10857

f i , j f_{i,j} fi,j表示前 i i i位模 N N N j j j的方案数

利用倍增思想,优化暴力转移

f 2 i , ( j ⋅ 1 0 i + k ) % N = ∑ f i , j ⋅ f i , k f_{2i,(j·10^i+k)\%N}=\sum f_{i,j}·f_{i,k} f2i,(j10i+k)%N=fi,jfi,k

固定 2 i 2i 2i,令 k = 1 0 i , g t = ∑ j ⋅ k % N = t f i , j , h t = f i , t k=10^i,g_t=\sum_{j·k\%N=t}f_{i,j},h_t=f_{i,t} k=10i,gt=jk%N=tfi,j,ht=fi,t

则转移变为 f 2 i , j + k = g j ∗ h k f_{2i,j+k}=g_j*h_k f2i,j+k=gjhk

标准可爱的卷积形式, F F T FFT FFT加速矩阵

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define ll long long
#define maxn 200100
#define mod 9973
#define N 10857

struct complex {
	double x, i;
	complex(){}
	complex( double X, double I ) {
		x = X, i = I;
	}
}A[maxn], B[maxn];

double pi = acos( -1.0 );

complex operator + ( complex a, complex b ) {
	return complex( a.x + b.x, a.i + b.i );
}

complex operator - ( complex a, complex b ) {
	return complex( a.x - b.x, a.i - b.i );
}

complex operator * ( complex a, complex b ) {
	return complex( a.x * b.x - a.i * b.i, a.x * b.i + a.i * b.x );
}

int len = 32768;
int r[maxn];

void FFT( complex *v, int opt ) {
	for( int i = 0;i < len;i ++ )
		if( i < r[i] ) swap( v[i], v[r[i]] );
	for( int i = 1;i < len;i <<= 1 ) {
		complex omega( cos( pi / i ), opt * sin( pi / i ) );
		for( int j = 0;j < len;j += ( i << 1 ) ) {
			complex w( 1, 0 );
			for( int k = 0;k < i;k ++, w = w * omega ) {
				complex x = v[j + k], y = v[j + k + i] * w;
				v[j + k] = x + y;
				v[j + k + i] = x - y;
			}
		}
	}
}

int qkpow( int x, int y ) {
	int ans = 1;
	while( y ) {
		if( y & 1 ) ans = ans * x % N;
		x = x * x % N;
		y >>= 1;
	}
	return ans;
}

bool check( int x ) {
	return x % 2 && x % 3 && x % 5 && x % 7 && x % 11 && x % 47;
}

void mul( int *f, int *g, int L ) {
	int k = qkpow( 10, L );
	for( int i = 0;i < N;i ++ ) {
		A[i * k % N] = complex( int( f[i] + A[i * k % N].x ) % mod , 0 );
		B[i] = complex( g[i], 0 );
		f[i] = 0;
	}
	FFT( A, 1 ), FFT( B, 1 );
	for( int i = 0;i < len;i ++ ) A[i] = A[i] * B[i];
	FFT( A, -1 );
	for( int i = 0;i < len;i ++ ) {
		f[i % N] = ( f[i % N] + (ll)( A[i].x / len + 0.5 ) ) % mod;
		A[i] = B[i] = complex( 0, 0 );
	}
}

int n;
int c[5] = { 1, 2, 3, 5, 7 };
int f[maxn], g[maxn];

int main() {
	scanf( "%d", &n );
	if( n == 1 ) return ! printf( "1\n" );
	else n --;
	int L = f[0] = g[1] = g[2] = g[3] = g[5] = g[7] = 1;
	int l = log2( len );
	for( int i = 0;i < len;i ++ )
		r[i] = ( r[i >> 1] >> 1 ) | ( ( i & 1 ) << ( l - 1 ) );
	while( n ) {
		if( n & 1 ) mul( f, g, L );
		mul( g, g, L );
		n >>= 1;
		L <<= 1;
	}
	int ans = 0;
	for( int i = 0;i < N;i ++ )
		for( int j = 0;j < 5;j ++ )
			if( check( i * 10 + c[j] ) )
				ans = ( ans + f[i] ) % mod;
			else;
	printf( "%d\n", ans );
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值