纪中DAY11做题小结
T1:粉刷(paint)
Description
鸡腿想到了一个很高(sha)明(bi)的问题,墙可以看作一个N*M的矩阵,有一些格子是有污点的。现在鸡腿可以竖着刷一次,覆盖连续的最多C列,或者横着刷一次,覆盖连续的最多R行。现在鸡腿把墙上的情况告诉你,请你告诉鸡腿最少要刷多少次才能刷干净!
Input
第1行,输入俩正整数N,M。
第2到N+1行,每行一个长度为M的字符串,每个字符可能是’.’表示干净的,或者’X’表示这个格子有污点。
第N+2行,输入俩正整数表示R和C。
Output
输出一行一个整数,表示鸡腿最少要刷几次。
Sample Input
输入1:1 9 XXXXXXXXX 2 3
输入2:
11 14 XXX..XXX..XXX. .X..X....X...X .X..X....X...X .X..X....X...X .X...XXX..XXX. .............. ...XX...XXX... ....X......X.. ....X....XXX.. ....X......X.. ...XXX..XXX... 1 2
Sample Output
输出1:1
输出2:
7
Data Constraint
对于50%的数据1≤N,M≤5;
对于100%的数据1≤N,M,R,C≤15。
简要思路:本题看上去像个贪心题,确实如此,然而,直接贪心可能会WRONG,因为我们要妥善处理行与列的关系。我们可以先用dfs来暴力枚举每行刷或者不刷的情况,同时可以加点剪枝优化一下(数据小,不用担心超时 ),再用贪心的方法处理列的情况(贪心法很显然,我不用讲了吧 )。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 25;
int n , m , r , c , ans;
char ss[N];
int room[N][N] , h[N] , hh[N] , l[N];
inline void read( int & res ) {
res = 0;
int pd = 1;
char a = getchar();
while ( a < '0' || a > '9' ) {
if ( a == '-' ) {
pd = -pd;
}
a = getchar();
}
while ( a >= '0' && a <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
a = getchar();
}
res *= pd;
return;
}
void dfs( int preh , int sum ) {
if ( preh > n ) {
int tot = sum;
memset( l , 0 , sizeof(l) );
for ( int j = 1 ; j <= m ; ++j ) {
for ( int i = 1 ; i <= n ; ++i ) {
if ( room[i][j] && h[i] ) {
l[j] = 1;
}
}
}
for ( int i = 1 ; i <= m ; ++i ) {
if ( !l[i] ) {
continue;
}
l[i] = 0;
for ( int j = 1 ; j <= c ; ++j ) {
l[min( i + j - 1 , m )] = 0;
}
tot++;
}
ans = min( ans , tot );
return;
}
dfs( preh + 1 , sum );
if ( h[preh] ) {//剪枝
for ( int i = 1 ; i <= r ; ++i ) {
h[min( i + preh - 1 , n )] = 0;
}
dfs( preh + 1 , sum + 1 );
for ( int i = 1 ; i <= r ; ++i ) {
if ( hh[min( i + preh - 1 , n )] == 1 ) {
h[min( i + preh - 1 , n )] = 1;
}
}
}
}
int main () {
read(n);
read(m);
for ( int i = 1 ; i <= n ; ++i ) {
scanf("%s",ss);
for ( int j = 1 ; j <= m ; ++j ) {
switch( ss[j - 1] ) {
case 'X' : {
room[i][j] = 1;
h[i] = 1;
hh[i] = 1;
break;
}
case '.' : {
room[i][j] = 0;
break;
}
}
}
}
read(r);
read(c);
ans = 0x3f3f3f3f;
dfs( 1 , 0 );
printf("%d",ans);
return 0;
}
T2:运算符(calc)
Description
鸡腿想到了一个很高(sha)明(bi)的运算符,那就是’!’,没错就是感叹号。他给了如下的定义:
1、n!k = n!(k-1) * (n-1)!k (n> 0 and k > 0)
2、n!k = 1 (n = 0)
3、n!k = n (k = 0)
现在鸡腿告诉你n和k你能告诉他n!k的不同约数个数有多少个吗?只要对1,000,000,009取模就可以了哦!
Input
一行,输入两个正整数n,k。
Output
一行,输出一个整数表示答案。
Sample Input
输入1:3 1
输入2:
100 2
Sample Output
输出1:4
输出2:
321266186
Data Constraint
对于30%的数据0 <n ≤ 10, 0 <k ≤ 10;
对于100%的数据0 <n ≤ 1000, 0 <k ≤ 100。
简要思路:初看这题总感觉它又是某道推柿子的数论难题,其实,它只是一道基础数论加上假的 DP的普通题,总体不算太难(然而我在考场上也只会打暴力 )。首先,由唯一分解定理,设
p
i
p_i
pi为素数,若数
n
n
n可表示为
p
1
k
1
∗
p
2
k
2
∗
.
.
.
∗
p
m
k
m
p_1^{k_1} * p_2^{k_2} * ... * p_m^{k_m}
p1k1∗p2k2∗...∗pmkm,则
n
n
n的因数个数为
(
k
1
+
1
)
∗
(
k
2
+
1
)
∗
.
.
.
∗
(
k
m
+
1
)
( k_1 + 1 ) * ( k_2 + 1 ) * ... * ( k_m + 1 )
(k1+1)∗(k2+1)∗...∗(km+1)。不管有没有思路,先欧拉筛一波素数再说 。
然后,分析题目要求,发现题目中的运算符
!
!
!满足无后效性的递归关系,对于题中的数据,我们只要记录它们每种素因子的个数即可,用
f
[
i
]
[
j
]
[
l
]
f[i][j][l]
f[i][j][l]表示
i
!
j
i!j
i!j的第
l
l
l个素数的个数。根据题目规定,未到边界时
i
!
j
=
i
!
(
j
−
1
)
∗
(
i
−
1
)
!
j
i!j = i!(j - 1) * (i - 1)!j
i!j=i!(j−1)∗(i−1)!j,而在乘法运算时积的因子数为两个乘数的因子和,故
f
[
i
]
[
j
]
[
l
]
=
f
[
i
−
1
]
[
j
]
[
l
]
+
f
[
i
]
[
j
−
1
]
[
l
]
f[i][j][l] = f[i - 1][j][l] + f[i][j - 1][l]
f[i][j][l]=f[i−1][j][l]+f[i][j−1][l]最后,处理边界值,所有
f
[
0
]
[
j
]
[
l
]
f[0][j][l]
f[0][j][l]均为零,所有
f
[
i
]
[
0
]
[
l
]
f[i][0][l]
f[i][0][l]值为数
i
i
i含
l
l
l因子的数目。
记得勤取模,数组不要开太大,
f
f
f必须用
i
n
t
int
int存贮,否则会
M
L
E
MLE
MLE。
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define mod 1000000009
using namespace std;
int n , k , pcnt , sum , tem;
int vis[1001] , prime[169];
int f[1001][101][169];
ll ans;
inline void read( int & res ) {
res = 0;
int pd = 1;
char a = getchar();
while ( a < '0' || a > '9' ) {
if ( a == '-' ) {
pd = -pd;
}
a = getchar();
}
while ( a >= '0' && a <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
a = getchar();
}
res *= pd;
return;
}
void pre() {
for ( int i = 2 ; i <= n ; ++i ) {
if ( !vis[i] ) {
prime[++pcnt] = i;
}
for ( int j = 1 ; j <= pcnt ; ++j ) {
if ( i * prime[j] > n ) {
break;
}
vis[i * prime[j]] = 1;
if ( !( i % prime[j] ) ) {
break;
}
}
}
return;
}
int main () {
read(n);
read(k);
pcnt = 0;
pre();
ans = 1;
for ( int i = 1 ; i <= n ; ++i ) {
for ( int j = 1 ; j <= pcnt ; ++j ) {
tem = i;
sum = 0;
while ( !( tem % prime[j] ) ) {
tem /= prime[j];
sum ++;
}
f[i][0][j] = sum;
}
}
memset( f[0] , 0 , sizeof(f[0]) );
for ( int l = 1 ; l <= pcnt ; ++l ) {
for ( int i = 1 ; i <= n ; ++i ) {
for ( int j = 1 ; j <= k ; ++j ) {
f[i][j][l] = f[i][j - 1][l] + f[i - 1][j][l];
f[i][j][l] %= mod;
}
}
}
for ( int i = 1 ; i <= pcnt ; ++i ) {
ans *= ( f[n][k][i] + 1 );
ans %= mod;
}
printf("%lld",ans);
return 0;
}
T3:倾斜的线
Description
Input
Output
Sample Input
6 15698 17433 112412868 636515040 122123982 526131695 58758943 343718480 447544052 640491230 162809501 315494932 870543506 895723090
Sample Output
193409386/235911335
Data Constraint
简要思路:这是一道数学题,合理地推导式子可以大大地简化问题的求解。
题目要求算出一种最接近的斜率,根据题意,可得:
∣
y
i
−
y
j
x
i
−
x
j
−
P
Q
∣
\vert{y_i-y_j\over x_i-x_j} - {P\over Q}\vert
∣xi−xjyi−yj−QP∣,题目要求我们求上式的最小值,通过转(xia)化(gao),可得:
∣
y
i
∗
Q
−
y
j
∗
Q
+
x
j
∗
P
−
x
i
∗
P
x
i
∗
Q
−
x
j
∗
Q
∣
\vert { y_i * Q-y_j*Q+x_j*P-x_i*P \over x_i * Q -x_j*Q }\vert
∣xi∗Q−xj∗Qyi∗Q−yj∗Q+xj∗P−xi∗P∣,进一步转化为:
∣
(
y
i
∗
Q
−
x
i
∗
P
)
−
(
y
j
∗
Q
−
x
j
∗
P
)
x
i
∗
Q
−
x
j
∗
Q
∣
\vert { (y_i * Q-x_i*P)-(y_j*Q-x_j*P) \over x_i * Q -x _j*Q }\vert
∣xi∗Q−xj∗Q(yi∗Q−xi∗P)−(yj∗Q−xj∗P)∣,将
y
i
∗
Q
−
x
i
∗
P
y_i * Q-x_i*P
yi∗Q−xi∗P当做点
i
i
i的新纵坐标,
x
i
∗
Q
x_i * Q
xi∗Q当做点
i
i
i的新横坐标,那么这问题可转化为求最小斜率的问题。
不过,我们还有一个问题,如何高效求出一组点中最小的斜率呢?
其实只要将所有点按纵坐标从小到大进行排序,取所有相邻两点的最小斜率即可。
如图,有三个点在
x
A
x_A
xA,
x
B
x_B
xB之间,不难发现,在两点纵坐标之间的点
C
C
C,
E
E
E,
D
D
D与
A
A
A,
B
B
B之间的连线中,总会有直线的斜率小于等于
k
A
B
k_{AB}
kAB,当然,也总会有直线的斜率大于等于
k
A
B
k_{AB}
kAB,所以,求相邻点的斜率最值即可得到整组点的斜率最值,求直线的最小斜率或最大斜率都可以用这招。
其它一些小细节就自己搞定吧,我也不赘述了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 2e5 + 5;
int n , p , q;
struct node{
double x;
double y;
int z;
}poi[N];
int xx[N] , yy[N];
bool cmp( node a , node b ) {
return a.y < b.y;
}
inline int gcd( int a , int b ) {
return b == 0 ? a : gcd( b , a % b );
}
inline void read( int & res ) {
res = 0;
int pd = 1;
char a = getchar();
while ( a < '0' || a > '9' ) {
if ( a == '-' ) {
pd = -pd;
}
a = getchar();
}
while ( a >= '0' && a <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( a - '0' );
a = getchar();
}
res *= pd;
return;
}
int main () {
//freopen( "slope.in" , "r" , stdin );
//freopen( "slope.out" , "w" , stdout );
read(n);
read(p);
read(q);
for ( int i = 1 ; i <= n ; ++i ) {
read(xx[i]);
read(yy[i]);
poi[i].x = double(xx[i]) * double(q);
poi[i].y = double(yy[i]) * double(q) - double(xx[i]) * double(p);
poi[i].z = i;
}
sort( poi + 1 , poi + 1 + n , cmp );
double minn = 1000000000.0;
int ans = 0;
for ( int i = 1 ; i <= n - 1 ; ++i ) {
if ( fabs(poi[i + 1].y - poi[i].y ) / fabs(poi[i + 1].x - poi[i].x ) < minn ) {
minn = fabs(poi[i + 1].y - poi[i].y) / fabs(poi[i + 1].x - poi[i].x);
ans = i;
} else if ( fabs(poi[i + 1].y - poi[i].y ) / fabs(poi[i + 1].x - poi[i].x ) == minn ) {
if ( fabs((double)yy[poi[i + 1].z] - (double)yy[poi[i].z]) / fabs((double)xx[poi[i + 1].z] - (double)xx[poi[i].z]) < fabs((double)yy[poi[ans + 1].z] - (double)yy[poi[ans].z]) / fabs((double)xx[poi[ans + 1].z] - (double)xx[poi[ans].z]) ) {
ans = i;
}
}
}
int aa = (int)yy[poi[ans + 1].z] - (int)yy[poi[ans].z];
int bb = (int)xx[poi[ans + 1].z] - (int)xx[poi[ans].z];
int d = gcd( aa , bb );
aa /= d;
bb /= d;
printf("%d/%d",aa,bb);
return 0;
}