CodeCraft-20 (Div. 2)
前言
比赛AC
简明题意
有n个人,每个人有个分数,最高分是m。现在其中一个同学可以修改所有人的分数,他想使得最高分不超过m且平均分不变的情况下尽可能把自己分数修改高。问最高是多少。
正文
平均分不变,等价于总分不变。直接让自己的分等于总分,其他人都0分就可以。然后和m比一下,如果比m大了就是m,否则就是总分。
代码
简明题意
有一个长度n<=5000的字符串,现在可以选一个k(1<=k<=n),选了k之后会依次反转原字符串的1到k,2到k+1…最终得到新的字符串。问k选几时,得到的新字符串字典序最小。
正文
k=2时,相当于冒泡排序,就是把a[1]移到了最后。
k>=2时,把1-k-1看成一个整体,这个整体会被移动到最后面。但注意每反转一次,那个整体的顺序就被颠倒一次。一共会被反转n-k+1次,所以当n-k+1是奇数时,就应该反转那个整体。
所以对于每个K就能O(1)得出操作后的字符串,而1<=k<=n,相当于一个字符串O(n)就能找到答案,复杂度是够的。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <string>
#include <vector>
using namespace std;
void solve ( )
{
int t;
cin >> t;
while ( t-- )
{
int n;
cin >> n;
string a;
cin >> a;
string ans;
int min_k;
for ( int k = 1 ; k <= n; k++ )
{
string x = a. substr ( 0 , k - 1 ) ;
if ( ( n - k + 1 ) % 2 == 1 ) reverse ( x. begin ( ) , x. end ( ) ) ;
string y = a. substr ( k - 1 , n - k + 1 ) ;
string b = y + x;
if ( ans == "" ) ans = b, min_k = k;
else if ( b < ans) ans = b, min_k = k;
}
cout << ans << endl;
cout << min_k << endl;
}
}
int main ( )
{
solve ( ) ;
return 0 ;
}
赛后补题
简明题意
给你两个多项式f(x)和g(x),用数组a[]给出f(x)的系数,b[]给出g(x)的系数。给出质数p,令h(x)=f(x)*g(x),现在需要你输出h(x)的任意一个不能被p整除的系数。
a数组两两互质,b数组也是。
正文
首先得直到两个多项式乘出来是什么。假设有k和l,那么
c
x
+
y
=
∏
i
=
0
x
+
y
a
i
b
x
+
y
=
a
0
b
x
+
y
+
a
1
b
x
+
y
−
1
+
.
.
.
+
a
x
b
y
+
.
.
.
+
a
x
+
1
b
y
−
1
+
a
x
+
y
b
0
c_{x+y}=\prod_{i=0}^{x+y}a_ib_{x+y}=a_0b_{x+y}+a_1b_{x+y-1}+...+a_xb_y+...+a_{x+1}b_{y-1}+a_{x+y}b_0
c x + y = i = 0 ∏ x + y a i b x + y = a 0 b x + y + a 1 b x + y − 1 + . . . + a x b y + . . . + a x + 1 b y − 1 + a x + y b 0
现在想要找到不能被p整除的一个c。 既然不能被p整除,那就让
C
x
+
y
C_{x+y}
C x + y 展开后的每一项都不能被p,可以吗?不行。。。。因为加起来后可能又能被p整除了。
通常做法是只让一项不能被p整除,其余全让他成为p的倍数。这样的话不能被p整除的项,就没法加上一个数使得他能被p整除。现在,我们就想要c的展开式中,只有一项不能被p整除。
这里可以让
a
x
a_x
a x 和
b
y
b_y
b y 不能被p整除,其余的
a
1
到
a
x
−
1
a_1到a_{x-1}
a 1 到 a x − 1 ,
b
1
到
b
x
−
1
b_1到b_{x-1}
b 1 到 b x − 1 全部能被p整除,这样就会发现上面的展开式中,只有
a
x
a
y
a_xa_y
a x a y 这一项不能被p整除,其余全部能被c整除。那么就可以得出此时的
c
x
+
y
c_x+y
c x + y 能被p整除。
让
a
x
a_x
a x 和
b
y
b_y
b y 不能被p整除,其余的
a
1
到
a
x
−
1
a_1到a_{x-1}
a 1 到 a x − 1 ,
b
1
到
b
x
−
1
b_1到b_{x-1}
b 1 到 b x − 1 全部能被p整除。这个怎么找呢?很简单,直接遍历ab数组,找到第一个不能被p整除的数字就可以了。
那么我们现在思考一下ab数组分别互质的意义。目前为止好像并没有用到这个性质。
就是告诉你,ab数组不能全被p整除。因为这样的话就没答案了
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <string>
#include <vector>
using namespace std;
const int maxn = 1e5 + 10 ;
void solve ( )
{
int n, m, p;
cin >> n >> m >> p;
int x = - 1 , y = - 1 ;
for ( int i = 1 ; i <= n; i++ )
{
int t;
scanf ( "%d" , & t) ;
if ( x == - 1 && t % p != 0 )
{
x = i;
}
}
for ( int i = 1 ; i <= m; i++ )
{
int t;
scanf ( "%d" , & t) ;
if ( y == - 1 && t % p != 0 )
{
y = i;
break ;
}
}
cout << x + y - 2 ;
}
int main ( )
{
solve ( ) ;
return 0 ;
}
简明题意
有一个n*n的矩阵。每个格子上有L、R、U、D、X五种字符,UDLR分别表示上下左右,X表示不走。
现在给出每个点作为起点,最终的到达点。如果某点出发不会停下来,就是-1
正文
如果(x,y)到达(a,b),说明ab一定是X。并且可以知道,(x,y)到(a,b)路径上的所有点,都应该是(a,b)。照这个思路,我们可以从(a,b)倒着搜回去,就能填好。那么怎么知道哪些点是X呢?直接判断,如果有个点(x,y)==(a,b),那么这个点就是X。
接下来只剩-1的没填了。我们发现,对于-1的连通块,只要连通块内的点>=2个,我们可以把其中相邻的两个互相指向。然后其他所有的点指向这两个点就可以了。
而实际上,我们可以发现,我们直接枚举-1的点,只要他周围存在一个-1,那么直接指向就可以了。这样就是对的。
现在说说答案不合法的情况。有两种情况,会不合法。
1.一个点-1,与他相邻的点却没有-1
2。例如,点1,1的值是1,3,但1,3却是-1-1.
读入不能用cin,否则tle
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <string>
#include <vector>
using namespace std;
const int maxn = 1000 + 10 ;
const int dir[ 4 ] [ 2 ] = { 1 , 0 , 0 , 1 , - 1 , 0 , 0 , - 1 } ;
int n, gx[ maxn] [ maxn] , gy[ maxn] [ maxn] ;
char ans[ maxn] [ maxn] ;
int X_pos_x, X_pos_y;
void dfs ( int x, int y, char k)
{
ans[ x] [ y] = k;
for ( int z = 0 ; z < 4 ; z++ )
{
int tx = x + dir[ z] [ 0 ] , ty = y + dir[ z] [ 1 ] ;
if ( tx >= 0 && tx <= n && ty >= 0 && ty <= n && ans[ tx] [ ty] == 0 && gx[ tx] [ ty] == X_pos_x && gy[ tx] [ ty] == X_pos_y)
{
if ( z == 0 ) dfs ( tx, ty, 'U' ) ;
if ( z == 1 ) dfs ( tx, ty, 'L' ) ;
if ( z == 2 ) dfs ( tx, ty, 'D' ) ;
if ( z == 3 ) dfs ( tx, ty, 'R' ) ;
}
}
}
void solve ( )
{
cin >> n;
for ( int i = 1 ; i <= n; i++ )
for ( int j = 1 ; j <= n; j++ )
scanf ( "%d%d" , & gx[ i] [ j] , & gy[ i] [ j] ) ;
for ( int i = 1 ; i <= n; i++ )
for ( int j = 1 ; j <= n; j++ )
if ( i == gx[ i] [ j] && j == gy[ i] [ j] )
{
X_pos_x = i, X_pos_y = j;
dfs ( i, j, 'X' ) ;
}
for ( int i = 1 ; i <= n; i++ )
for ( int j = 1 ; j <= n; j++ )
if ( ans[ i] [ j] == 0 && gx[ i] [ j] == - 1 )
{
bool ok = 0 ;
int x = i, y = j;
for ( int z = 0 ; z < 4 ; z++ )
{
int tx = x + dir[ z] [ 0 ] , ty = y + dir[ z] [ 1 ] ;
if ( gx[ tx] [ ty] == - 1 )
{
if ( z == 0 ) ans[ x] [ y] = 'D' ;
if ( z == 1 ) ans[ x] [ y] = 'R' ;
if ( z == 2 ) ans[ x] [ y] = 'U' ;
if ( z == 3 ) ans[ x] [ y] = 'L' ;
ok = 1 ;
}
}
if ( ! ok)
{
cout << "INVALID" ;
return ;
}
}
for ( int i = 1 ; i <= n; i++ )
for ( int j = 1 ; j <= n; j++ )
if ( ans[ i] [ j] == 0 )
{
cout << "INVALID" ;
return ;
}
cout << "VALID" << endl;
for ( int i = 1 ; i <= n; i++ )
for ( int j = 1 ; j <= n; j++ )
{
printf ( "%c" , ans[ i] [ j] ) ;
if ( j == n) printf ( "\n" ) ;
}
}
int main ( )
{
solve ( ) ;
return 0 ;
}
简明题意
共有n个人,现在从中选p个人作为参赛选手,k个人作为观众。
第i个人作为参赛选手可以获得a[i]分,第i个人作为观众坐在第j个位置可以获得s[i][j]分。现在需要你最大化这个得分。
正文
代码
总结