题目链接:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=104302#overview
Problem A
Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
Sample Input
2 1
8 4
4 7
Sample Output
0
1
0
此题为一道博弈题(威佐夫博弈)
当x = i*(sqrt(5)+1)/2, y = x+i时为必输局。
#include <stdio.h>
#include <math.h>
#define LL long long
int main ( )
//威佐夫博弈,当x = i*(sqrt(5)+1)/2 y = x+i时为必输局
{
LL a, b;
while ( ~ scanf ( "%I64d%I64d", &a, &b ) )
{
if ( a > b )
{
LL t = a;
a = b;
b = t;
}
LL ans = ( LL )( b-a )*( sqrt ( 5.0 )+1 )/2;
printf ( "%d\n", ans == a ? 0 : 1 );
}
}
Problem B
Description
Factorial of an integer is defined by the following function
f(0) = 1
f(n) = f(n - 1) * n, if(n > 0)
So, factorial of 5 is 120. But in different bases, the factorial may be different. For example, factorial of 5 in base 8 is 170.
In this problem, you have to find the number of digit(s) of the factorial of an integer in a certain base.
Input
Input starts with an integer T (≤ 50000), denoting the number of test cases.
Each case begins with two integers n (0 ≤ n ≤ 106) and base (2 ≤ base ≤ 1000). Both of these integers will be given in decimal.
Output
For each case of input you have to print the case number and the digit(s) of factorial n in the given base.
Sample Input
5
5 10
8 10
22 3
1000000 2
0 100
Sample Output
Case 1: 3
Case 2: 5
Case 3: 45
Case 4: 18488885
Case 5: 1
题意:将n!化成m进制的位数。
分析:
求位数实际就是求出m的多少次方,所以可以用取对数的方法求,
例如:bit10(5!)= log10(1*2*3*4*5) = 3
那么求m进制,就只要将10改成m就行,
另外一个性质是:
log a(b) = log10 a/log 10 b
那么就可以合并得到(lg1+lg2+…+lgn)/lgm
#include <stdio.h>
#include <math.h>
#define eps 1e-9
const int maxn = 1000005;
double ans[maxn];
int main ( )
{
int T, n, b, cas = 1;
ans[0] = 0;
for ( int i = 1; i < maxn; i ++ )
//将所有log10(1*...*i)的值先算出来
ans[i] = ans[i-1]+log ( i );
scanf ( "%d", &T );
while ( T -- )
{
scanf ( "%d%d", &n, &b );
printf ( "Case %d: ", cas ++ );
if ( n == 0 ) //0!为1
{
printf ( "1\n" );
continue ;
}
double t = ans[n]/log ( b );
long long val = ( long long )t;
if ( t-val >= eps ) //进位,用1e-5过不了,所以用了1e-9
val ++;
printf ( "%lld\n", val );
}
return 0;
}
Problem C
C题就是枚举起点和终点,去异或值最大的就行,没什么好讲的。
#include <stdio.h>
#define LL long long
const int maxn = 105;
LL a[maxn];
int main ( )
{
int n;
LL ans = 0;
scanf ( "%d", &n );
for ( int i = 0; i < n; i ++ )
scanf ( "%lld", &a[i] );
for ( int i = 0; i < n; i ++ )
{
for ( int j = i; j < n; j ++ )
{
LL xr = 0;
for ( int k = i; k <= j; k ++ )
xr ^= a[k];
if ( ans < xr )
ans = xr;
}
}
printf ( "%lld", ans );
return 0;
}
Problem D
Description
Igor has fallen in love with Tanya. Now Igor wants to show his feelings and write a number on the fence opposite to Tanya's house. Igor thinks that the larger the number is, the more chance to win Tanya's heart he has.
Unfortunately, Igor could only get v liters of paint. He did the math and concluded that digit d requires ad liters of paint. Besides, Igor heard that Tanya doesn't like zeroes. That's why Igor won't use them in his number.
Help Igor find the maximum number he can write on the fence.
Input
The first line contains a positive integer v(0 ≤ v ≤ 106). The second line contains nine positive integers a1, a2, ..., a9(1 ≤ ai ≤ 105).
Output
Print the maximum number Igor can write on the fence. If he has too little paint for any digit (so, he cannot write anything), print -1.
Sample Input
Input
5
5 4 3 2 1 2 3 4 5
Output
55555
Input
2
9 11 1 12 5 8 9 10 6
Output
33
Input
0
1 1 1 1 1 1 1 1 1
Output
-1
题意:你有数字1-9,每次刷出某个数字需要消耗d[i]升油漆,你总共有v升油漆,你需要找出能刷出的数字最大为多少,一个数字都不能刷出就输出-1.
分析:
这道题我开始一直找位数最多的,其实这样是不完全对的,因为能得到的数字的位数是已经确定了的,你需要贪心高位的数字让其变大,所以找所有数字中需要油漆最少的,那样位数肯定是最大的m,但是有可能会有剩余,那么就可以利用这些剩余油漆来使高位变大(此题浪费一个多小时)。
#include <stdio.h>
#include <string.h>
#define INF 0x7fffffff
const int maxn = 15, N = 1000005;
int d[maxn];
char str[N];
int main ( )
{
int v;
scanf ( "%d", &v );
for ( int i = 1; i < 10; i ++ )
scanf ( "%d", &d[i] );
int m = INF, ansi;
for ( int i = 1; i < 10; i ++ )
{
if ( m >= d[i] ) //找到需要油漆最少的,且数字最大的
{
m = d[i];
ansi = i;
}
}
if ( v/m == 0 ) //一个都刷不了
{
printf ( "-1" );
return 0;
}
strcpy ( str, "" );
int cnt = 0, mod = v%d[ansi]; //余数
m = v/m; //能刷ansi此数字的个数
while ( mod > 0 )
{
int flag = 0;
for ( int i = 9; i > ansi; i -- ) //从后往前找,尽量让高位大
if ( mod+d[ansi] >= d[i] )
{
flag = 1; //标记找到数字
mod += d[ansi]-d[i]; //余数缩小
m --; //取ansi个数减少,不能省
str[cnt ++] = i+'0';
break ;
}
if ( flag == 0 )
break ;
}
str[cnt] = '\0';
//printf ( "%d\n", m );
printf ( "%s", str );
for ( int i = 0; i < m; i ++ )
printf ( "%d", ansi ); //还要取m个ansi
return 0;
}
/* 9 2 3 5 5 5 5 5 5 5 */
Problem E
Description
Given an N*N(N<=1000)chessboard where you want to place chess knights.
On this chessboard you have to apply M(M<=100000) operations:
Input
The first line contains a single integer T, the number of test cases.
For each case,
The first line contains integer N, M.
The next M lines contain the operation in following form.
C a b x: place chess knight on cell(a,b), the value of the knight is x. (1<=a,b<=n, 1<=x<=100000)
It grants that cell(a,b) has no knight before the operation.
Q a b: answer the maximum value of knight which is connected with knight(a,b), include itself.
If cell (a,b)has no knight, just answer -1. A knight is directly connected with the knight on the left,
right, up and down. Two knights are connected if they have been directly connected or
interconnected through some other connected knights.
The initial chessboard is empty.
Output
For each question, output the answer in one line.
Sample Input
1
3 7
C 2 2 1
Q 2 2
C 1 2 2
Q 2 2
C 3 3 3
Q 3 3
Q 1 1
Sample Output
1
2
3
-1
题意:
有两种操作:C a b c将cell(a,b)这个格子的值更新为c,只会更新一次,
Q a b表示查询此格子所在连通块的最大值。
注意连通块是与上下左右相连的,我开始将其相成图,这跟图是不一样的,图是相连的是连通的。
#include <stdio.h>
#include <string.h>
const int maxn = 1005*1005;
int father[maxn];
int mx[maxn], vis[maxn], n;
int dx[] = { 1, 0, -1, 0 }, dy[] = { 0, 1, 0, -1 };
void init ( int m ) //初始化
{
for ( int i = 1; i <= m; i ++ )
father[i] = i, vis[i] = mx[i] = 0;
}
int find ( int x )
{
int r = x;
while ( r != father[r] )
r = father[r];
int i = x, j;
while ( i != r )
{
j = father[i];
father[i] = r;
i = j;
}
return r;
}
void merge ( int x, int y )
{
int fx = find ( x ), fy = find ( y );
if ( fx != fy )
{
father[fx] = fy;
if ( mx[fy] < mx[fx] ) //更新父节点的最大值
mx[fy] = mx[fx];
}
}
int check ( int x, int y )
{
return x <= 0 || x > n || y <= 0 || y > n;
}
int main ( )
{
int T, m, a, b, c, x, y;
char op[2];
scanf ( "%d", &T );
while ( T -- )
{
scanf ( "%d%d", &n, &m );
init ( n*( n+2 ) ); //初始化的时候需要大一些
while ( m -- )
{
scanf ( "%s", op );
if ( op[0] == 'C' )
{
scanf ( "%d%d%d", &a, &b, &c );
x = a*n+b; //n可能为0,所以不能用n-1
vis[x] = 1;
mx[x] = c;
for ( int i = 0; i < 4; i ++ ) //注意只有相邻才是连通的
{
int na = dx[i]+a, nb = dy[i]+b;
if ( check ( na, nb ) ) //检查边界
continue ;
y = na*n+nb;
if ( vis[y] == 0 )
continue ;
merge ( x, y );
}
}
else
{
scanf ( "%d%d", &a, &b );
x = a*n+b;
if ( vis[x] == 0 ) //此点没赋值过
printf ( "-1\n" );
else
printf ( "%d\n", mx[ find ( x ) ] );
//查找此连通块的最大值
}
}
}
return 0;
}