考试复盘
今天的题其实蛮温柔的
考完试预估分 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,i−1是否相邻的信息拿到
自己的 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 mid−x和 n − x + 1 n-x+1 n−x+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 [i−1,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 [i−1,i+1,i],1种方法
f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j]
-
不与 i i i相邻且减少一对非法相邻数对,有 j − 1 j-1 j−1种方法
g [ i + 1 ] [ j − 1 ] g[i+1][j-1] g[i+1][j−1]
-
不与i相邻且不改变非法相邻数对数,有 i − j i-j i−j种方法
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][j−1]
-
不与 i i i相邻且不改变非法相邻数对数,有 i − j − 1 i-j-1 i−j−1种摆放方法
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,(j⋅10i+k)%N=∑fi,j⋅fi,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=∑j⋅k%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=gj∗hk
标准可爱的卷积形式, 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;
}