1001.追星族 Accepts: 21 Submissions: 122
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
度度熊最近迷上了S明星,恰逢她正在巡回演出,得知所有的演出安排后,它希望一场不落的看完所有的演出。
每场演出的地点 (Xi,Yi) 以及时间Ti,这些信息事先都已经公布。但唯一的问题是,单位时间内度度熊的移动速度只有可怜的1。它当然希望离自己的偶像越近越好,所以它希望在所有的演出时刻,它距离演出地点距离的最大值可以最小。度度熊在时间点0时刻出发,并且它可以选择任何一个位置作为起点。
生活在一个格子化的二次元中,度度熊是这样计算距离的:|x1−x2|+|y1−y2|
Input
第一行一个整数T,表示T组数据。
每组样例的第一行有一个整数N(1≤N≤50000),表示演出的场数。
接下来的N行,每行包括三个整数Xi,Yi,Ti(−109≤Xi,Yi≤109,0≤Ti≤109),描述一场演出的地点与时间。数据中存在演出时间相同或者演出地点相同的数据。
Output
对于每组数据,输出两行:
第一行输出:"Case #i:"。i代表第i组测试数据。
第二行输出最小的最大距离。为了尽量精确,用分数A/B的形式表示,其中A和B不可继续化简。
Sample Input
2
2
1 1 2
1 5 1
2
1 1 2
1 5 10
Sample Output
Case #1:
3/2
Case #2:
0/1
题目分析:
注意到,一个点的曼哈顿距离范围实际上是一个旋转了45°的正方形,并且同样方向的矩形的交依旧是一个矩形,这样我们这道题就可以做了。
先将所有点按照时间排序。
然后,我们二分答案,也就是半径d,然后我们将每个点扩展d的范围变成一个正方形,然后我们从最后一个正方形往前面的矩形求交。
先将最后一个矩形再扩展 tn−tn−1 的范围,这就是最后我们能处于的合法位置的范围,再和第 n−1 个正方形求交,得到的就是上一步的合法位置范围,这是一个矩形。不断重复上述过程直到到达第一个矩形,中间如果出现不合法情况,就说明这个距离不够,调整下界。否则调整上界。
关键部分其实是如何方便的表达一个矩形。考虑到直线y=x,y=-x,我们可以将一个边长为2d的正方形表示为:x+y-d,x+y+d,y-x-d,y-x+d。这四条直线代表的半平面求出的交就是我们的正方形了,然后矩形的表示也是类似的。对于矩形的求交,实际上就是对四条直线表示的值更新。
最后要注意的是,答案只可能是x/1或者x/2的形式,所以一开始所有数*2来使得我们的过程不涉及小数。
my code:
#include
#include
#include
#include
#include
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , n
const int MAXN = 100005 ;
struct Rectangle {
LL x1 , x2 , y1 , y2 ;
LL t ;
void print () {
printf ( "%I64d %I64d %I64d %I64d\n" , x1 , x2 , y1 , y2 ) ;
}
void input () {
LL x , y ;
scanf ( "%I64d%I64d%I64d" , &x , &y , &t ) ;
x *= 2 ;
y *= 2 ;
t *= 2 ;
x1 = x + y ;
x2 = x + y ;
y1 = y - x ;
y2 = y - x ;
}
bool operator < ( const Rectangle &a ) const {
return t < a.t ;
}
Rectangle get ( LL d ) {
Rectangle tmp ;
tmp.x1 = x1 - d ;
tmp.x2 = x2 + d ;
tmp.y1 = y1 - d ;
tmp.y2 = y2 + d ;
return tmp ;
}
} ;
Rectangle a[MAXN] ;
int n ;
int check ( LL d ) {
Rectangle now = a[n] ;
now = now.get ( d ) ;
for ( int i = n - 1 ; i >= 1 ; -- i ) {
int t = a[i + 1].t - a[i].t ;
now = now.get ( t ) ;
Rectangle tmp = a[i].get ( d ) ;
if ( now.x2 < tmp.x1 || tmp.x2 < now.x1 ) return 0 ;
if ( now.y2 < tmp.y1 || tmp.y2 < now.y1 ) return 0 ;
now.x1 = max ( now.x1 , tmp.x1 ) ;
now.x2 = min ( now.x2 , tmp.x2 ) ;
now.y1 = max ( now.y1 , tmp.y1 ) ;
now.y2 = min ( now.y2 , tmp.y2 ) ;
}
return 1 ;
}
void solve () {
scanf ( "%d" , &n ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
a[i].input () ;
}
sort ( a + 1 , a + n + 1 ) ;
LL l = 0 , r = 1e10 ;
while ( l < r ) {
LL m = ( l + r ) >> 1 ;
if ( check ( m ) ) r = m ;
else l = m + 1 ;
}
if ( l % 2 ) printf ( "%I64d/2\n" , l ) ;
else printf ( "%I64d/1\n" , l / 2 ) ;
}
int main () {
int T ;
scanf ( "%d" , &T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
printf ( "Case #%d:\n" , i ) ;
solve () ;
}
return 0 ;
}
1002.连接的管道 Accepts: 739 Submissions: 3405
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
老 Jack 有一片农田,以往几年都是靠天吃饭的。但是今年老天格外的不开眼,大旱。所以老 Jack 决定用管道将他的所有相邻的农田全部都串联起来,这样他就可以从远处引水过来进行灌溉了。当老 Jack 买完所有铺设在每块农田内部的管道的时候,老 Jack 遇到了新的难题,因为每一块农田的地势高度都不同,所以要想将两块农田的管道链接,老 Jack 就需要额外再购进跟这两块农田高度差相等长度的管道。
现在给出老 Jack农田的数据,你需要告诉老 Jack 在保证所有农田全部可连通灌溉的情况下,最少还需要再购进多长的管道。另外,每块农田都是方形等大的,一块农田只能跟它上下左右四块相邻的农田相连通。
Input
第一行输入一个数字T(T≤10),代表输入的样例组数
输入包含若干组测试数据,处理到文件结束。每组测试数据占若干行,第一行两个正整数 N,M(1≤N,M≤1000),代表老 Jack 有N行*M列个农田。接下来 N 行,每行 M 个数字,代表每块农田的高度,农田的高度不会超过100。数字之间用空格分隔。
Output
对于每组测试数据输出两行:
第一行输出:"Case #i:"。i代表第i组测试数据。
第二行输出 1 个正整数,代表老 Jack 额外最少购进管道的长度。
Sample Input
2
4 3
9 12 4
7 8 56
32 32 43
21 12 12
2 3
34 56 56
12 23 4
Sample Output
Case #1:
82
Case #2:
74
题目分析:
裸的最小生成树……我比较愚蠢,把所有的边都保存了下来,然后排序,然后机智的学弟用了计数排序。
my code:
#include
#include
#include
#include
#include
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , n
const int MAXN = 1005 ;
const int MAXE = 2000005 ;
struct Edge {
int u , v , c ;
Edge () {}
Edge ( int u , int v , int c ) : u ( u ) , v ( v ) , c ( c ) {} ;
bool operator < ( const Edge &e ) const {
return c < e.c ;
}
} ;
Edge E[MAXE] ;
int cntE ;
int G[MAXN][MAXN] ;
int idx[MAXN][MAXN] ;
int p[MAXE] ;
int n , m ;
int find ( int x ) {
int o = x ;
while ( p[o] != o ) o = p[o] ;
int ans = o , tmp ;
while ( p[x] != ans ) {
tmp = p[x] ;
p[x] = ans ;
x = tmp ;
}
return ans ;
}
void solve () {
int cnt = 0 ;
cntE = 0 ;
scanf ( "%d%d" , &n , &m ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= m ; ++ j ) {
idx[i][j] = ++ cnt ;
p[cnt] = cnt ;
scanf ( "%d" , &G[i][j] ) ;
if ( i > 1 ) E[cntE ++] = Edge ( idx[i - 1][j] , idx[i][j] , abs ( G[i][j] - G[i - 1][j] ) ) ;
if ( j > 1 ) E[cntE ++] = Edge ( idx[i][j - 1] , idx[i][j] , abs ( G[i][j] - G[i][j - 1] ) ) ;
}
}
sort ( E , E + cntE ) ;
int ans = 0 ;
for ( int i = 0 ; i < cntE ; ++ i ) {
int u = find ( E[i].u ) ;
int v = find ( E[i].v ) ;
if ( u != v ) {
ans += E[i].c ;
p[u] = v ;
}
}
printf ( "%d\n" , ans ) ;
}
int main () {
int T ;
scanf ( "%d" , &T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
printf ( "Case #%d:\n" , i ) ;
solve () ;
}
return 0 ;
}
1003.棋盘占领 Accepts: 937 Submissions: 2201
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
百小度最近迷恋上了一款游戏,游戏里有一个n*m的棋盘,每个方格代表一个城池。
一开始的时候我们有g支军队,驻扎并占领了其中某些城池。然后我们可以在这些被占领城池的基础上,吞并占领周围的城池。
而其吞并占领的规则是这样的——一旦一个城池A相邻的上下左右四个城池中至少存在两个被占领,且这两个被占领的城池有公共点,那么城池A也将被占领。
比如我们用1表示初始的占领状态,0表示初始的未占领状态。
那么——
10
01
会最终会变成
11
11
而101则保持为101不变
现在告诉你一张地图一开始所有被占领城池的信息,问你最后多少个城池会被我们占领。
Input
第一行为T,表示输入数据组数。
下面T组数据,对于每组数据,
第一行是两个数n,m(1≤n,m≤500),表示国土的大小为n*m。
第二行是一个整数g(1≤g≤1000),表示我们一开始占领的城池数。
然后跟随g行,第i行一对整数x,y(1≤x≤n,1≤y≤m),表示占领的第i个城池的坐标。
Output
对第i组数据,输出
Case #i:
然后输出一行,仅包含一个整数,表示最终有多少个城池被占领。
Sample Input
4
2 2
2
1 1
2 2
3 3
3
1 1
2 3
3 2
2 4
5
1 1
1 1
1 2
1 3
1 4
2 4
2
1 1
2 4
Sample Output
Case #1:
4
Case #2:
9
Case #3:
4
Case #4:
2
题目分析:bfs就好……
my code:
#include
#include
#include
#include
#include
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , n
const int MAXN = 505 ;
struct Node {
int x , y ;
Node () {}
Node ( int x , int y ) : x ( x ) , y ( y ) {}
} ;
Node Q[MAXN * MAXN] ;
int G[MAXN][MAXN] ;
int head , tail ;
int n , m , k ;
int path[4][2] = { { 1 , 1 } , { 1 , -1 } , { -1 , 1 } , { -1 , -1 } } ;
int check ( int x , int y ) {
return x >= 1 && x <= n && y >= 1 && y <= m ;
}
void bfs () {
while ( head != tail ) {
Node u = Q[head ++] ;
for ( int i = 0 ; i < 4 ; ++ i ) {
int x = u.x + path[i][0] ;
int y = u.y + path[i][1] ;
if ( check ( x , y ) == 0 || G[x][y] == 0 ) continue ;
if ( G[x][u.y] == 0 ) {
G[x][u.y] = 1 ;
Q[tail ++] = Node ( x , u.y ) ;
}
if ( G[u.x][y] == 0 ) {
G[u.x][y] = 1 ;
Q[tail ++] = Node ( u.x , y ) ;
}
}
}
}
void solve () {
int x , y ;
head = tail = 0 ;
scanf ( "%d%d%d" , &n , &m , &k ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= m ; ++ j ) {
G[i][j] = 0 ;
}
}
for ( int i = 1 ; i <= k ; ++ i ) {
scanf ( "%d%d" , &x , &y ) ;
G[x][y] = 1 ;
Q[tail ++] = Node ( x , y ) ;
}
bfs () ;
int ans = 0 ;
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= m ; ++ j ) {
ans += G[i][j] ;
}
}
printf ( "%d\n" , ans ) ;
}
int main () {
int T ;
scanf ( "%d" , &T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
printf ( "Case #%d:\n" , i ) ;
solve () ;
}
return 0 ;
}
1004.魔法因子 Accepts: 215 Submissions: 1208
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
有人说:人类是自己一步步进化的,而数学是上帝亲手创造的。度度熊最近也正沉醉于数学之美中,它发现了一种神奇的数字,取名曰:魔法因子。
将因子记为X,如果有一些整数与这些因子做乘法后,结果仍然是整数,同时,结果数字的首位和末位会换交换位置,而其他位置上的数字恰好不变!这时X被认为是一个魔法因子。需要注意的是,用来相乘的这些整数不会含有前导0,但是如果交换的结果有前导0,又恰好是乘法的结果,这时仍然认为X是这个整数的魔法因子。度度熊认为1不是个魔法因子,因为所有的数都可以符合这个条件,这一点都不好玩。
比如,X = 3.1312,有1875 * 3.1312 = 5871。
度度熊现在希望知道对于一个因子X,究竟有多少个整数可以满足这个条件,使其成为魔法因子。由于它还不会长度超过10位数的乘法,只需要求出长度不超过10的整数即可。
Input
第一行一个整数T,表示包含T组数据。 每组数据包含一个实数X(010,X≠1),同时为了避免精度问题,小数点后的数字不会超过6位。
Output
每组数据,对于每组数据,先输出一行
Case #i:
然后输出符合条件的整数的个数,如果个数不为0,在第二行输出所有符合条件的整数,按数字大小升序排列,用空格隔开,如果个数为0,只输出一行。
Sample Input
3
3.1312
3.1415
0.3
Sample Output
Case #1:
3
1875 1876875 1876876875
Case #2:
0
Case #3:
2
1428570 2857140
题目分析:
首先要将输入的小数转化成分数的形式(直接用double输入转化成整数不行,听说要加一个0.0000005,然后我就是因为这个跪了好几发,最后换成字符串输入转化就过了)。假设分母为y1,分子为x1(假设是最简形式,除以gcd过)。
我们只要,枚举一个首位a,一个个位c,以及数的位数x,然后假设中间未知的部分为b,则我们可以列出一个方程:
my code:
#include
#include
#include
#include
#include
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , n
const int L = 1000000 ;
LL p[15] ;
LL S[L] , top ;
LL gcd ( LL a , LL b ) {
return b ? gcd ( b , a % b ) : a ;
}
LL f ( LL pp , LL a , LL c , LL x1 , LL y1 ) {
LL x = a * pp * x1 - c * pp * y1 + c * x1 - a * y1 ;
LL y = ( y1 - x1 ) * 10 ;
if ( x == 0 ) return 0 ;
else if ( x > 0 && y < 0 || x < 0 && y > 0 ) return -1 ;
else {
if ( x % y ) return - 1 ;
else return x / y ;
}
}
char s[100] ;
void solve () {
int x = 0 , y = 1 , loc = 0 ;
top = 0 ;
scanf ( "%s" , s ) ;
int n = strlen ( s ) ;
for ( int i = 0 ; i < n ; ++ i ) {
if ( s[i] >= '0' ) {
x = x * 10 + s[i] - '0' ;
if ( loc ) y *= 10 ;
} else loc = 1 ;
}
int g = gcd ( x , y ) ;
int x1 = x / g ;
int y1 = y / g ;
for ( int i = 1 ; i <= 9 ; ++ i ) {
for ( int a = 1 ; a <= 9 ; ++ a ) {
for ( int c = 0 ; c <= 9 ; ++ c ) {
LL b = f ( p[i] , a , c , x1 , y1 ) ;
if ( b < 0 || b * 10 >= p[i] ) continue ;
LL t = a * p[i] + b * 10 + c ;
S[top ++] = t ;
}
}
}
printf ( "%d\n" , top ) ;
sort ( S , S + top ) ;
for ( int i = 0 ; i < top ; ++ i ) {
printf ( "%I64d%c" , S[i] , i < top - 1 ? ' ' : '\n' ) ;
}
}
int main () {
int T ;
p[0] = 1 ;
for ( int i = 1 ; i <= 12 ; ++ i ) {
p[i] = p[i - 1] * 10 ;
}
scanf ( "%d" , &T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
printf ( "Case #%d:\n" , i ) ;
solve () ;
}
return 0 ;
}
1005.序列变换 Accepts: 695 Submissions: 3322
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
我们有一个数列A1,A2...An,你现在要求修改数量最少的元素,使得这个数列严格递增。其中无论是修改前还是修改后,每个元素都必须是整数。 请输出最少需要修改多少个元素。
Input
第一行输入一个T(1≤T≤10),表示有多少组数据
每一组数据:
第一行输入一个N(1≤N≤105),表示数列的长度
第二行输入N个数A1,A2,...,An。
每一个数列中的元素都是正整数而且不超过106。
Output
对于每组数据,先输出一行
Case #i:
然后输出最少需要修改多少个元素。
Sample Input
2
2
1 10
3
2 5 4
Sample Output
Case #1:
0
Case #2:
1
题目分析:对所有ai -= i,然后求最长不下降序列就行了。
my code:
#include
#include
#include
#include
#include
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , n
const int MAXN = 100005 ;
int a[MAXN] ;
int S[MAXN] , top ;
int n ;
int search ( int x , int l = 1 , int r = top ) {
while ( l < r ) {
int m = ( l + r ) >> 1 ;
if ( S[m] > x ) r = m ;
else l = m + 1 ;
}
return l ;
}
void solve () {
scanf ( "%d" , &n ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
scanf ( "%d" , &a[i] ) ;
a[i] -= i ;
}
top = 0 ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( !top || S[top] <= a[i] ) S[++ top] = a[i] ;
else {
int x = search ( a[i] ) ;
S[x] = a[i] ;
}
}
printf ( "%d\n" , n - top ) ;
}
int main () {
int T ;
scanf ( "%d" , &T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
printf ( "Case #%d:\n" , i ) ;
solve () ;
}
return 0 ;
}
1006.翻转游戏 Accepts: 15 Submissions: 143
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
度度熊最近迷上一个小游戏:Flip it。游戏的规则很简单,在一个N*M的格子上,有一些格子是黑色,有一些是白色。每选择一个格子按一次,格子以及周围边相邻的格子都会翻转颜色(边相邻指至少与该格子有一条公共边的格子),黑变白,白变黑。
度度熊希望把所有格子都变成白色的。不幸的是,有一些格子坏掉了,无法被按下。这时,它可以完成游戏吗?
Input
第一行一个整数T,表示T组数据。
每组数据开始于三个整数N,M,K(1≤N,M,K≤256),分别表示格子的高度和宽度、坏掉格子的个数。接下来的N行,每行一个长度M的字符串,表示格子状态为’B’或‘W’。最后K行,每行两个整数Xi,Yi(1≤Xi≤N,1≤Yi≤M),表示坏掉的格子。
Output
对每组样例,对于每组数据,先输出一行Case #i: (1≤i≤T)
如果可以成功,输出YES,否则输出NO。
Sample Input
2
3 3 0
WBW
BBB
WBW
3 3 2
WBW
BBB
WBW
2 2
3 2
Sample Output
Case #1:
YES
Case #2:
NO
题目分析:1006不会