Test Message

b969: hello, everyone

內容

給你一串人名,請你一一向他們問好。


輸入

輸入只有二行。第一行含有要問候的人名,人名之間以空白隔開,人名中不含空白。第二行含有問候語。

john mary jacob david
hello

輸出

對於每一個所提供的人名,請輸出 問候語 + “, “ + 人名 於一行。請參考範例輸出。

hello, john
hello, mary
hello, jacob
hello, david


解題思路

簡單的字串處理。


完整程式碼

AC (2ms, 108KB)
#include <stdio.h>
#include <string.h>
#define MAX 10000

char name[MAX], hello[MAX], * hs, * h;

int main()
{
fgets(name, MAX, stdin);
fgets(hello, MAX, stdin);
h = hello + strlen(hello);
*h++ = ',', * h++ = ' ';
hs = h;
for (int i = 0; name[i]; i++)
{
if (name[i] == ' ')
* h = '\0', puts(hello), h = hs;
else
*h++ = name[i];
}
*h = '\0', puts(hello);
return 0;
}

b944: 好想上廁所(男廁篇)

內容

有一天,小明急急忙忙的衝到了某間廁所,(不要問我為什麼是小明)

卻發現廁所的某種規律!(不要問我為什麼小明急著上廁所還可以發現規律)

首先,他發現男生們上廁所都會儘量相隔一個小便斗,(不要問我為什麼會這樣)

除非他們找不到能夠相鄰一個小便斗的小便斗,

才會勉為其難的使用其他的小便斗,

若是沒有小便斗,就只好轉身向著遙遠彼方的閃耀廁所奔去,

這嚴重地影響了男生廁所的效率(其實並沒有),

小明想要寫一個程式,觀察男生廁所的使用狀況~~(其實你是變態是吧?)


輸入

一開始給予一個整數 n (0<n<21),作為小便斗的數量上限。

然後給予兩個整數 a,b (0<=a,b<2147483647),作為下一個男生的編號和使用時間。

測資讀入直到 EOF。所有的男生搜尋空閒小便斗時都會從編號最小的開始找。

如果使用時間為 0,依然會佔用小便斗直到下一個人進來。

3
1 5
2 4
3 3
4 2
5 1

輸出

每次輸出共有兩行或三行,

若該次輸入的使用者找不到小便斗,則輸出” Not enough”,

接下來一行是全部小便斗的使用者編號,

一行是全部小便斗的使用者剩餘時間,

若該小便斗沒有人使用,則兩者皆輸出 0。

Number: 1 0 0
Time: 5 0 0

Number: 1 0 2
Time: 4 0 4

Number: 1 3 2
Time: 3 3 3

Not enough
Number: 1 3 2
Time: 2 2 2

Not enough
Number: 1 3 2
Time: 1 1 1


解題思路

有點麻煩的流程控制,搜尋小便斗時先找兩側都沒人的空便斗,沒有再找空便斗,每次都在最後才將時間遞減 (也就是輸出完該輪答案後)。

注意人上廁所的時間可以為 0 ,這種情況下要輸出編號:n 時間:0,然後將他在下一輪開前清除。


完整程式碼

AC (11ms, 100KB)
#include <stdio.h>

typedef struct Node
{
int User, Time;
} Toilet;

int n, a, b;
Toilet list[22];

inline char serchToilet()
{
for (int i = 1; i <= n; i++)
{
if (list[i].User) continue;
for (int j = i; j <= n; j++)
{
if (!list[j - 1].User && !list[j].User && !list[j + 1].User)
{
list[j].User = a;
list[j].Time = b;
return 1;
}
}
list[i].User = a;
list[i].Time = b;
return 1;
}
return 0;
}

int main()
{
scanf(" %d", &n);
while (scanf(" %d %d", &a, &b) == 2)
{
if (!serchToilet())
puts(" Not enough");
printf("Number:");
for (int i = 1; i <= n; i++)
printf(" %d", list[i].User);
printf("\n Time:");
for (int i = 1; i <= n; i++)
{
printf(" %d", list[i].Time);

if (list[i].Time < 2)
list[i].User = list[i].Time = 0;
else
list[i].Time--;
}
putchar('\n');
}
return 0;
}

b938: kevin 愛殺殺

內容

kevin 身為工具人 + 一日隊輔

一定要帶給隊員們最大的娛樂

所以他帶了一個活動 叫 盲人摸象

一開始 n 個人站成一列

編號為 1 ~ n

每次 kevin 都會叫編號 k 的人 把他後面的人殺掉

但是… 人實在太多了 0u0

隊伍蔓延了 1 公里多

而 kevin 視力很差 看不了那麼遠

所以請你告訴 kevin 被殺掉的是誰

如果 這個這個人已經死了 or 他是最後一個人

請輸出 “0u0 …… ?”


輸入

輸入的第一行 有 2 個整數 n, m (1 <= n, m <= 10 ^ 6)

代表有 n 個人 站成一排 編號為 1~n

接下來一行有 m 個數字 k1 k2 … km (1 <= k <= n)

代表 kevin 要殺掉 第 k 個人的下一個人

5 4
1 1 5 4

輸出

每次輸出被殺掉的人的編號

如果不合法 輸出”0u0 …… ?”

2
3
0u0 …… ?
5


解題思路

給每個元素一個整數指向他下一次要殺的編號,如果把他殺了,就將目標改成該死者的目標。


完整程式碼

AC (0.3s, 7.7MB)
#include <stdio.h>
#define MAX 1000010

typedef struct Node
{
int Next;
char Alive;
}Player;

Player list[MAX];
int n, m;

int main()
{
while (scanf(" %d %d", &n, &m) == 2)
{
for (int i = 1; i <= n; i++)
{
list[i].Alive = 1;
list[i].Next = i + 1;
}
list[n + 1].Alive = 0;
for (int i = 0, k; i < m; i++)
{
scanf(" %d", &k);
if (list[k].Alive && list[list[k].Next].Alive)
{
printf("%d\n", list[k].Next);
list[list[k].Next].Alive = 0;
list[k].Next = list[list[k].Next].Next;
}
else
{
puts("0u0 ...... ?");
}
}
}
return 0;
}

b936: Kevin 愛橘子

內容

有一天 Kevin 走在路上,隨手摘了一顆路邊樹上的橘子
當他正準備咬下去時,忽然一個瞬間他停了下來
一看不得了,這顆橘子裡面竟然有 N 片橘子!
這讓 Kevin 很苦惱,因為他昨天看到電視上說人一天只需要吃 M 片橘子就能攝取到足夠的營養
他不想要獨享這顆橘子,他希望能讓越多的人能至少吃到 M 片橘子
但是由於 Kevin 走在路上甚麼都沒帶,因此他每次只能將橘子分成兩半

例如若要分一顆有 N 片的橘子
當 N 為偶數,則 Kevin 會將其分成 N/2 和 N/2 兩堆
當 N 為奇數,則 Kevin 會將其分成 (N-1)/2 和 (N+1)/2 兩堆

譬如當橘子有 10 片時,他會將橘子分成 5 片和 5 片
而當橘子有 11 片時,他會將橘子分成 5 片和 6 片

再說了這麼多之後,你身為 Kevin 的朋友,早就已經餓的望著橘子發楞了
但是 Kevin 還沒決定好總共到底有多少人能夠吃到橘子
為了趕快吃到橘子,你能幫幫 Kevin 嗎?


輸入

多筆輸入 保證輸入筆數 < 10^6
每行有兩個數字 N 和 M
代表一顆裡面有 N 片的橘子
和 Kevin 希望每個人都至少能夠吃到 M 片橘子
1 <= N <= 10^15
1 <= M <= 10^15

2 1
10 3
12 3
1024 2
201612 28

輸出

請輸出這顆橘子總共可以分給多少人並滿足 Kevin 的要求
(人數包含 Kevin)

2
2
4
512
4096


解題思路

用遞迴模擬 Kevin 剝橘子的過程直到要剝的那片除 2 時小於目標大小時返回 1。

ZJ 改版之後伺服器似乎變弱很多,同樣的程式碼舊版 0.9s 過的放到現在要 1.7s,這導致這題時間變得比較緊張,善用位元運算加速。


完整程式碼

AC (1.7s, 116KB)
#include <stdio.h>

long long n, m;

long long dfs(long long piece)
{
if ((piece >> 1) < m)
return 1;
if (piece & 1)
return dfs((piece + 1) >> 1) + dfs((piece - 1) >> 1);
else
return dfs(piece >> 1) << 1;
}

int main()
{
while (scanf(" %lld %lld", &n, &m) == 2)
{
printf("%lld\n", n < m ? 0 : dfs(n));
}
return 0;
}

b924: kevin 愛畫畫

內容

kevin 很喜歡畫畫 但是 kevin 畫圖有個原則 就是他只用一筆畫把圖畫完

幾天前 kevin 喜歡的女生送了 kevin 一張圖

這張圖上有一些點 點之間還有一些邊

他很想幫這張圖上色

但又必須堅守一筆畫的原則

請你告訴 kevin

他能不能用一筆畫畫完這張圖 (( 經過所有的邊

而且每條邊只能經過一次


輸入

第一行有兩個數字 n , m

代表圖上有 n 個點 m 條邊

接下來有 m 行

每行有兩個數字 a b

代表 a b 兩點互相連接

(( n < 10000 , m < 10^7 , 1 <= a , b <= n

((a b 間可能存在不只一條邊

((保證圖為連通圖

4 5
1 2
2 4
3 4
3 1
1 4

4 6
1 2
2 4
3 4
3 1
1 4
2 3

輸出

如果可以一筆畫完成 輸出”YES”

否則輸出”NO”

YES

NO


解題思路

用基數排序統計好該點被線段經過的次數,之後結算時如果除 2 不餘 0 代表這個點必須是投或尾,如果這種點超過 3 個,表示 Kevin 無法一筆畫畫完,因為一筆畫只有一個頭和一個尾。


完整程式碼

AC (1.2s, 112KB)
#include <stdio.h>
#include <memory.h>

int list[10000];

int main()
{
int n, m, f, t, count;
while (scanf(" %d %d", &n, &m) == 2)
{
memset(list + 1, 0, sizeof(int) * n);
for (int i = 0; i < m; i++)
{
scanf(" %d %d", &f, &t);
list[f]++, list[t]++;
}
count = 0;
for (int i = 1; i <= n; i++)
{
if (list[i] & 1)
{
count++;
if (count > 2)
break;
}
}
puts(count < 3 ? "YES" : "NO");
}
return 0;
}

b923: stack 堆疊的模板題

內容

我想要請你實作 stack 的幾種操作

1.刪除堆頂元素

2.輸出頂端元素

3.丟數字進堆疊


輸入

第一行有一個 n (n <= 100000)

接下來有 n 行

每一行一開始有一個數字,代表哪一種操作

如果數字為 1,代表刪除操作

如果數字為 2,請輸出當前 stack 的頂端元素

如果數字為 3,請在讀入一個整數丟進堆疊

5
3 10
3 15
2
1
2

輸出

對於每個操作 2 請輸出一個數字

15
10


解題思路

測資不大,直接開 n 的大值大小的陣列來模擬 stack 即可。


完整程式碼

AC (2ms, 80KB)
int stack[100001];

int main()
{
int n, sTop;
char cmd;
while (scanf(" %d", &n) == 1)
{
sTop = 0;
while (n--)
{
scanf(" %c", &cmd);
if (cmd == '1')
sTop--;
else if (cmd == '2')
printf("%d\n", stack[sTop]);
else
scanf(" %d", &stack[++sTop]);
}
}
return 0;
}

b911: 我想跟Kevin借筷子系列4

內容

成功許凱皓總是喜新厭舊,日子一天一天過去,他就越想把那些筷子砍掉

他前幾天已經把他的 n 支筷子砍成長度分別為 1、2、3、4 , …. , n

他現在決定把他們砍光,他每一次操作可以從剩下的筷子中挑選一支或者多支,同時砍掉一個相同的長度

例如: 長度分別為 1、2、3 的 3 支筷子,可以把長度為 2 和 3 的筷子同時砍掉 2,得到長度為 1、0、1 的三支筷子

再把長度為 1 的兩支筷子砍掉 1,就把所有筷子砍完了!

成功許凱皓很怕麻煩,他想知道最少要幾次操作才能把所有筷子砍完


輸入

多筆輸入

第一行有一個整數 n (0 <= n <= 10^18)

代表有幾支筷子

3
4

輸出

輸出最少要幾次操作才能把筷子砍完

2
3


解題思路

因為只要長度相同,一次可以砍無限根筷子,而只要砍法正確,大筷子砍完必能將小筷子也砍完,所以再求的其實就是最長筷子最少要砍多少次。

而要求一個筷子最少要砍幾次其實就是不斷的對半砍,砍到剩 1 再砍一次變成 0 就行了。

所以程式目標就是去模擬不斷將筷子對半砍直到砍成 0 的過程,也就是將輸入不斷的除以 2 直到歸 0。

另外上述不斷砍半的過程也可以轉換成數學公式,用公式解

最低次數 = Floor(Log2n)+1


完整程式碼

連除版

AC (2ms, 104KB)
#include <stdio.h>

int main()
{
unsigned long long n;
int count;
while (scanf(" %llu", &n) == 1)
{
count = 0;
while (n)
{
n >>= 1;
count++;
}
printf("%d\n", count);
}
return 0;
}

公式解版

AC (2ms, 132KB)
#include <stdio.h>
#include <math.h>

int main()
{
double n;
while (scanf(" %lf", &n) == 1)
{
printf("%d\n", (int)log2(n) + 1);
}
return 0;
}

b897: 10219 - Find the ways !

內容

一個美國人、一個法國人、和一個英國人來到了孟加拉的首都達卡。他們搭計乘車去觀光。三個觀光客聊起了城市的景觀。那個美國人為紐約的高聳建築感到騎傲。他對朋友們吹噓:「你知道帝國大廈只花了三個月就蓋好了嗎?」

「真的?」法國人說:「巴黎的艾菲爾鐵塔一個月就蓋好了呢!」(然而事實上,鐵塔於 1887 年一月動工。40 個工程師和設計師在艾菲爾的指導下工作了兩年。鐵塔於 1989 年三月完成。)

「真有趣!」英國人說:「倫敦的白金漢宮兩個星期就蓋好了!」

這時計程車經過了一個大型貧民窟 (然而,在孟加拉我們稱之為 Bostii)。「那是什麼?什麼時候蓋的?」英國人問那孟加拉司機。

「我不知道!」司機回答說:「昨天還沒看到呀!」。

在孟加拉,非法貧民窟的存在一直是個大問題。政府一直試著拆除這些貧民窟並將其中居民正式安置於市郊的重劃區。但是他們找不到任何的方法來拆除這些貧民窟!

現在,你能想像你是貧民窟的拆除者嗎?要將 k 個貧民窟中的 n 個貧民窟拆除有幾種方法?假設有 10 個貧民窟,你獲准拆除其中的 5 個,你有 252 個方式,這只是個 3 位數字。你的工作就是找出拆除貧民窟的方法數是幾位數字。


輸入

輸入檔含有一筆或多筆測資。

每筆測資一行,含有兩個整數 n (n ≥ 1) 及 k (1 ≤ k ≤ n)。

20 5
100 10
200 15

輸出

針對每筆測資,輸出一個所要求的數字於一行。這個數字可存於一個整數,也就是它小於 231 − 1。

5
14
23


解題思路

數學題,有 3 個重點公式

  1. Log(a * b) = Log(a) + Log(b)
  2. Log(a / b) = Log(a) - Log(b)
  3. n 的位數 = Floor(Log10n) + 1

然後如以下範例展開

以 10! / 4! 為例,將階層展開

(10 * 9 * 8 * 7) / (6 * 5 * 4 * 3)

取 Log

Log10(10 * 9 * 8 * 7 / 4 * 3 * 2 * 1)

因為公式 1 和 2 所以可以再將它展開成

(Log1010 + Log109 + Log108 + Log107) - (Log104 + Log103 + Log102 + Log101)

又因為公式 3 所以位數就是

Floor(Log1010 + Log109 + Log108 + Log107 - Log104 - Log103 - Log102 - Log101) + 1

計算結果

Floor(1 + 0.954 + 0.903 + 0.845 - 0.602 - 0.477 - 0.301 - 0) + 1
= Floor(2.322) + 1 = 2 + 1 = 3
就能得到它的位數了。

接下來就是簡單的把公式轉換成程式碼,取階層時記得取小的那區即可。


完整程式碼

AC (66ms, 176KB)
#include <stdio.h>
#include <math.h>

int main()
{
register double log;
int n, k, max;
while (scanf(" %d %d", &k, &n) == 2)
{
log = 0, max = n < k - n ? n : k - n;
for (int i = 0; i < max; i++)
log += log10(k - i) - log10(i + 1);
printf("%d\n", (int)log + 1);
}
return 0;
}

b893: 勘根定理

內容

高中數學的等級明顯比國中高出了許多,這點在小紫進入了板橋高中之後她就清楚地體會到了 ── 以往國中輕鬆考都可以 90 分以上甚至滿分的數學,居然會不及格!
  但儘管小紫受到了莫大的打擊,她依然想努力地把數學顧好,再加上他們的數學老師誇下海口,讓這次數學段考排名是三位數的同學兩個兩個分組,可以在第二次段考的時候針對各組進步最多分的那一位同學來做整組相對應的學期總成績加分,這種種契機讓小紫燃起了鬥志,於是她找了班上一位數學很好的同學波路特石與她同組,想讓第二次段考能夠有 90 分。

這天,小紫正好與波路特石在討論數學。
  「欸欸,這個『甚根定理』是什麼東西啊?」
  小紫對波路特石提出了疑問。
  「ㄜ……那個是『勘』根定理。」
  波路特石無奈地回應。
  「沒差啦,我問你這個是什麼意思!」
  「……」波路特石儘管感到很頭痛,但還是繼續解釋了下去:「勘根定理的概念其實很簡單,就是在講『當一個連續的函數 f(x)滿足 f(a)×f(b)<0 的時候,必可以在 a 和 b 之間找到至少一個根滿足 f(x)=0』,順帶一提,a、b 是實數。」
  「為什麼?」
  針對小紫再次的提問,波路特石在紙上畫出了一個直角座標,並在 x 軸的上方和下方各點出了一個點。
  「因為我已經給出了『函數是連續的』這個條件,也就是說這個函數必不會間斷……你應該知道當 y 是 x 的函數,一個 x 最多只能對應到一個 y 吧?」
  「知道。」
  「好,那你模擬看看,假設你今天要從下面這個點,走到上面這個點,並且你的路徑一定要是連續的,又不能繞出去這兩點之間,請問你有辦法不經過 x 軸就到達上面的點嗎?」
  「當然不能啊!」小紫在反駁波路特石的剎那間忽然靈光一閃、恍然大悟:  「我懂了!所以在上面這個點和下面這個點之間一定會經過 x 軸,也就是 f(x)=0 的解嘛!」
  「沒錯,這樣你應該就懂了。」
  「謝謝!」

小紫非常的興奮,她又理解了一個新的定理,但有關於勘根定理的題目實在是太少了,於是她想自己出給自己題目,不過她卻沒辦法確認答案的正確性,又不想再打擾最近忙著程式競賽還願意花時間教自己數學的波路特石。

請你撰寫一個程式,幫小紫判斷她出的「多項式函數」所有根的範圍分別介於哪兩個連續整數之間,好讓小紫可以驗證正確答案。


輸入

每筆輸入有以空格隔開的 6 個整數,分別為 a、b、c、d、e、f,代表 ax5+bx4+cx3+dx2+ex+f,來表示小紫出給自己的函數題目。

0 0 6 -25 -29 20

輸出

請輸出這個函數所有的根分別介於哪兩個「連續」整數之間,並由小排到大,每一個根的範圍佔一行,每一行裡的兩個數字以空格隔開,小的在左。
若有無限多組根請輸出「Too many… = =”」(不含引號);
若函數無實根請輸出「N0THING! >\\\<」(不含引號)。
若根恰為整數,請重複輸出兩次此根,並以空格隔開。

-2 -1
0 1
5 5


解題思路

先建好 pow 表,之後要使用直接查表省時間。

再來用迴圈窮舉帶入公式符合條件時輸出即可。

注意 ax5 有可能會超過 int 上限、當輸入皆為 0 時要出輸出 Too many… = =


完整程式碼

AC (2ms, 112KB)
#include <stdio.h>

int pow[81][6];

int main()
{
for (int i = -40; i <= 40; i++)
{
pow[40 + i][1] = i;
pow[40 + i][2] = i * i;
pow[40 + i][3] = pow[40 + i][2] * i;
pow[40 + i][4] = pow[40 + i][3] * i;
pow[40 + i][5] = pow[40 + i][4] * i;
}
long long fa, fb, a;
int b, c, d, e, f;
char noAns;
while (scanf(" %lld %d %d %d %d %d", &a, &b, &c, &d, &e, &f) == 6)
{
if (a || b || c || d || e || f)
{
noAns = 1;
fa = a * pow[0][5] + b * pow[0][4] + c * pow[0][3] + d * pow[0][2] + e * pow[0][1] + f;
for (int i = 1; i <= 80; i++)
{
fb = a * pow[i][5] + b * pow[i][4] + c * pow[i][3] + d * pow[i][2] + e * pow[i][1] + f;
if (!fb)
printf("%d %d\n", pow[i][1], pow[i][1]), noAns = 0;
else if ((fa ^ fb) < 0 && fa)
printf("%d %d\n", pow[i - 1][1], pow[i][1]), noAns = 0;
fa = fb;
}
if (noAns)
puts("N0THING! >\\\\\\<");
}
else
{
puts("Too many... = =\"");
}
}
return 0;
}

b884: 電腦教室的傑克

內容

傑克是位認真的學生,每次在資訊社個總是全神貫注的聆聽,但他坐的心情會隨著位置改變

現在給你傑克位置(x,y),老師位置(0,0),有一個距離公式 yee,yee=100-r*r

r=√Δx+Δy


輸入

第一行有一個 n,代表有 n 行.測資,接下來有 n 行 x 和 y 代表傑克的位置。

1
20 20

輸出

如果 0<yee<=30,輸出 “sad!” ,如果 30<yee<=60,輸出”hmm~~”,如果 60<yee<100,輸出”Happyyummy”,如果 yee 超出範圍,輸出”evil!!”(不含雙引號)

hmm~~


解題思路

看似複雜,不過代換一下公式 :

  1. yee = 100 - r²
  2. yee = 100 - (√(x + y))² (r = (x + y)²)
  3. yee = 100 - (x + y)
  4. yee = 100 - x - y

就會發現其實只需要四則運算就能得到答案了,剩下就是基本的條件判斷。


完整程式碼

AC (2ms, 112KB)
#include <stdio.h>

int main()
{
int n, x, y, yee;
scanf(" %d", &n);
while (n--)
{
scanf(" %d %d", &x, &y);
yee = 100 - x - y;
if (yee <= 0) puts("evil!!");
else if (yee <= 30) puts("sad!");
else if (yee <= 60) puts("hmm~~");
else if (yee < 100) puts("Happyyummy");
else puts("evil!!");
}
return 0;
}
1161718192027