【海量集訓&&題解】複賽前夕 day3——考試題解
專輯:海量集訓->題解
一、綠巨人的跳躍
題目描述
綠巨人想跳上一座高 H 米的大樓, 但跳躍要消耗一定的憤怒值。
這棟大樓每一層高 h 米, 即距地面 0 米, h 米, 2h 米…到 H 米, 都有
一層地板可供綠巨人落腳。 保證 h 是 H 的因數。
綠巨人有兩種跳躍方式: 每次跳 h1 米, 一次消耗 w1 的憤怒值; 每次
跳 h2 米, 一次消耗 w2 的憤怒值。從頭到尾綠巨人只能選擇一種跳躍方式。
綠巨人不能在半空中再次起跳, 所以必須落在離跳躍最高點最近且在最高
點下方的樓層上。 綠巨人需要保證每次跳躍之後憤怒值都是非負的。
現在綠巨人憤怒值為 0, 正在補充憤怒值。 他希望你告訴他至少要補
充多少憤怒值才能跳上這棟大樓。
【 輸入格式(jump.in)】
輸入一行包含 6 個正整數: H,h,h1,w1,h2,w2。
【 輸出格式(jump.out)】
輸出一行包含 1 個整數: 綠巨人至少要補充的憤怒值。 如果綠巨人永
遠跳不上這棟大樓, 輸出“ Puny Hulk” ( 不包含引號) 。
【 樣例輸入 1】
4 2 2 3 3 4
【 樣例輸出 1】
6
【 樣例輸入 2】
10000 4 2 1 4 10000
【 樣例輸出 2】
25000000
【 樣例輸入 3】
200 200 1 1 2 1
【 樣例輸出 3】
Puny Hulk
【 解釋】
樣例 1 中選擇第一種跳躍方式, 跳 2 次可跳上樓頂, 花費 6。 樣例 2
中選擇第二種跳躍方式, 跳 2500 次可跳上樓頂, 花費 25000000。 樣例 3
中由於兩種跳躍方式都小於樓層高度, 所以綠巨人無法跳上樓頂。
【 資料規模與約定】
保證 H 是 h 的倍數。
10%的資料保證綠巨人無法跳上樓頂。
另外 30%的資料保證綠巨人兩種跳躍方式都能一次跳上樓頂。
另外 30%的資料保證 h1 和 h2 都是 h 的倍數。
100%的資料保證 1<=H,h,h1,w1,h2,w2<=10000
分析:
【 資料一】
直接輸出 -1 即可。
【 資料二】
因為一次足夠, 所以取 w1 和 w2 的最小值輸出。
【 資料三】
專門給不會上取整的小盆友的分, 詳見資料四。
【 資料四】
根據題意, 對一種方式, 每跳一次能夠跳上的樓層數是相等的, 所以
可以把跳的高度直接當成跳的樓層數。 這一步是一次除法。 之後算總的步
數顯然也是一次除法, 複雜度是 O(1)的, 答案為:
如果h1<h或者h2<h只要取用其中一個值即可
如果兩個都小於h就輸出那個奇奇怪怪的東西
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define MAXX 100000000
int minn=MAXX;
int H,h,h1,w1,h2,w2;
int main(){
freopen("jump.in","r",stdin);
freopen("jump.out","w",stdout);
scanf("%d %d %d %d %d %d",&H,&h,&h1,&w1,&h2,&w2);
if (h1>=h)
minn=min(minn,(H/h)/min((h1/h),H/h)*w1);
if (h2>=h)
minn=min(minn,(H/h)/min((h2/h),H/h)*w2);
if (minn==MAXX) printf("Puny Hulk");
else printf("%d",minn);
fclose(stdin);
fclose(stdout);
return 0;
}
二、雷神的錘子
題目描述
雷神正在一片平原上揮舞它質量為﹢∞的錘子。 除了質量奇特, 還有
一點奇特, 就是它每次砸向地面, 形成的坑都是矩形。
雷神共用錘子砸了 n 次, 每次的力度不一定相同。 我們用 d 代表力度
的大小, 表示錘子砸下去後, 形成的坑的深度是 d。 開始平原上所有地方
的深度都是 0。 如果此次砸下的區域有地方已經被砸過了, 那麼這部分的
深度等於各次砸下的 d 的最大值。 假設砸下去只會影響對應矩形的深度,
不會影響矩形外的平原。
現在他派你去把砸出的所有的坑都填上, 你需要先告訴他把坑填上,
也就是使所有地方的深度都是 0, 需要他提供多少單位體積的土。
【 輸入格式(hammer.in)】
輸入第一行包含一個正整數 n。
接下來 n 行, 每行 5 個整數描述一次砸下, x0,y0,x1,y1,d。 (x0,y0)為矩形
的左下角, (x1,y1)為矩形的右上角。
【 輸出格式(hammer.out)】
輸出一行包含 1 個整數: 需要多少單位體積的土。
【 樣例輸入 1】
10
0 3 3 1
【 樣例輸出 1】
9
【 樣例輸入 2】
30
0 1 1 4
0 0 1 1 5
0 0 2 2 3
【 樣例輸出 2】
14
【 解釋】
樣例 1 中形成一個 331 的坑。
樣例 2 中(0,0),(1,1)對應的矩形區域共被砸了 3 次, 深度為 3 次 d 的最
大值 5。
【 資料規模與約定】
保證 x0<=x1,y0<=y1。
30%的資料保證 n=1。
另外 30%的資料保證所有的 d 都相等。
100%的資料保證|x0|,|y0|,|x1|,|y1|<=300,n<=500,1<=d<=109。
分析:
【 資料一】
n=1, 直接算出答案即可。
【 資料二】
因為所有的 d 都相等, 所以都可以視為 1。 我們把平面上可能用到的
點分成 600600 個 11 的正方形。這樣對每次砸下都更新所有影響到的點,
最後統計影響到的個數。
【 資料三】
在資料二的基礎上把標記改為“當前深度” , 然後對每次砸下更新所
有點的深度,如果<d 則令它等於 d。最後把深度相加。複雜度是 O(300^2*n)。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define plus 301
LL a[701][701];
LL n;
LL ans=0;
int main(){
freopen("hammer.in","r",stdin);
freopen("hammer.out","w",stdout);
scanf("%lld",&n);
for (LL i=1;i<=n;i++){
LL x,y,xx,yy,d;
scanf("%lld %lld %lld %lld %lld",&x,&y,&xx,&yy,&d);
for (LL i=x+plus;i<xx+plus;i++)
for (LL j=y+plus;j<yy+plus;j++)
a[i][j]=max(a[i][j],d);
}
for (LL i=0;i<=700;i++)
for (LL j=0;j<=700;j++)
ans+=a[i][j];
printf("%lld",ans);
return 0;
}
備註:為什麼有負數啊啊啊啊!
三、鷹眼的箭
題目描述
鷹眼用自己超強的視力, 看到了平面上的 n 個敵方火力點, 第 i 個點
的座標是(Xi,Yi)。 他需要用自己的百發百中的箭把這些火力點摧毀。
鷹眼的箭非常強大, 射一次箭可以把飛行路線路線上所有的火力點直
接摧毀。 假設每支箭的飛行路線都是射線。 鷹眼可以在平面上任意位置射
箭, 但是由於一些原因, 鷹眼的箭的方向只能平行於 x 軸或 y 軸或與某一
座標軸成 45° 角。
現在鷹眼想知道, 如果只能從一個位置射箭, 不限射出多少支箭, 能
夠摧毀火力點最多有多少個。 共有 q 個查詢, 每個查詢互不干擾。 如果鷹
眼所在位置正好有一個火力點, 無論往哪個方向射箭, 這個火力點都會被
摧毀。 為了減少讀寫時間, 你需要按照下列公式求出最後輸出的值, 其中
的 表示的是異或( C 和 C++中是^, Pascal 中是 xor) 。 設第 i 次詢問的正
確答案是 ansi, 那麼你需要輸出的是:
(ans+1 )^(ans2 +2)^ … ^(ansq ^q)
【 輸入格式(arrow.in)】
輸入第一行包含 2 個正整數: n 和 q。
接下來 n 行, 第 i 行包含 2 個整數: Xi 和 Yi。
接下來 q 行, 第 i 行包含 2 個正整數: QXi 和 QYi, 代表一次查詢, 鷹
眼所在位置是(QXi,QYi)。
【 輸出格式(arrow.out)】
輸出 1 行表示所有查詢結果的異或和。
【 樣例輸入 1】
4 2
0 0
2 2
0 2
2 0
0 1
1 1
【 樣例輸出 1】
5
【 樣例輸入 2】
1 1
233333 233333
233333 233333
【 樣例輸出 2】
2
【 解釋】
下頁圖表示樣例 1 的點分佈情況, 不同顏色的箭頭表示不同查詢能夠摧毀
的火力點。 第一次查詢結果為 2, 第二次為 4, 所以應輸出(2+1)^ (4+2)=5。
【 資料規模與約定】
保證所有火力點都不重合
30%的資料保證 n,q<=1000。
70%的資料保證座標|Xi|,|Yi|,|QXi|,|QYi|<=100000。
100%的資料保證|Xi|,|Yi|,|QXi|,|QYi|<=109, n,q<=100000。
【 提示】
輸入資料規模較大, 建議避免使用速度慢的輸入方式, 如 cin。
分析:【 資料一】
沿 x 軸方向射箭, 摧毀的是 x 值相同的點。 沿 y 軸方向射箭, 摧毀的
是 y 值相同的點。 沿斜方向射箭, 摧毀的是 x+y 相同的點或 x-y 相同的點。
暴力列舉每個點, 看是否 x, y, x+y, x-y 中有其一和給定位置相同, 複雜
度 O(nq)。
【 資料二】
在資料一的基礎上, 把列舉去除, 改為統計每個不同的 x 值、 y 值、
x+y 值、 x-y 值。 這樣一次查詢即為求 4 個統計的數的和。 由於座標範圍不
大, 陣列開到 410^5 即可。 注意要判斷查詢是否與給定的某個點重合,
如果重合了, 這個點將會被算 4 次, 所以答案要減去 3。 這一步可以用排
序或者 STL 中的 set。
【 資料三】
座標範圍變大, 陣列不再可用, 這時注意到統計的值只有 O(n)個, 所
以把陣列改為 map 或者用排序+二分的方法, 就可以輕鬆統計個數。 複雜
度為 O(nlogn)。
程式碼:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int ans=0;
map <pair < int,int >,bool> a;
map <int,int> b;
map <int,int> c;
map <int,int> d;
map <int,int> e;
int main(){
freopen("arrow.in","r",stdin);
freopen("arrow.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++){
int x,y;
scanf("%d %d",&x,&y);
b[x]++;
c[y]++;
d[x+y]++;
e[x-y]++;
a[make_pair(x,y)]=1;
}
for (int i=1;i<=m;i++){
int x,y;
scanf("%d %d",&x,&y);
int sum=0;
if (a[make_pair(x,y)]) sum=-3;
sum+=b[x]+c[y]+d[x+y]+e[x-y];
if (i==1) ans=sum+1;
else ans^=sum+i;
}
cout<<ans;
return 0;
}
備註:為什麼有負數啊啊啊啊!
四、超級英雄的戰鬥
題目描述
美國隊長和鋼鐵俠正在與很多外星人戰鬥。 這場戰鬥的持續時間是 t
分鐘, 每分鐘美國隊長和鋼鐵俠都可能消滅 0 個或 1 個外星人。 我們用一
個長度為 t 的字串 S 來描述這場戰鬥, 每個字元代表一分鐘內發生的情
況: M 表示只有美國隊長消滅了 1 個外星人, G 表示只有鋼鐵俠消滅了 1
個外星人, T 表示兩人同時消滅了 1 個外星人, 不存在某 1 分鐘兩人都沒
有消滅外星人。
鋼鐵俠知道一般情況下他們兩個的戰鬥能力比是 m:g, 表示在相同時
間內如果美國隊長消滅了 mk 個敵人, 鋼鐵俠應當能消滅 gk 個。 戰鬥之
後, 鋼鐵俠發現, 並不是所有連續時間段都符合一般情況的。 用 S 的一個
子串 S2=S[l…r]表示一個連續時間段, 我們稱 S2 是“好” 的, 當且僅當在
第 l 分鐘到第 r 分鐘( 兩端均包含) 美國隊長消滅的敵人數和鋼鐵俠消滅
的敵人數之比恰好等於 m:g。
鋼鐵俠需要計算最長的“好” 的 S 的非空子串, 最長的“ 好” 的子串
的數量, 以及 S 的所有“好” 的非空子串的數量。 如果字串一樣但位置
不同分開計算數量。
【 輸入格式(fight.in)】
輸入第一行包含 3 個非負整數: t,m 和 g。
第二行包含一個長度為 t 的字串 S, 含義如前所說。
【 輸出格式(fight.out)】
輸出兩行。 第一行包含 3 個整數 l,r 和 cnt, 表示 S 的子串 S[l…r]是最
長的“好” 子串( S 的編號從 1 開始) , 如果有多個輸出最左邊的子串,
以及與它等長的“ 好”的子串的數量。 第二行包含 1 個整數, 表示 S 的“好”
的非空子串數量。 如果不存在“好” 的非空子串, 只輸出”A weird fight”( 不
包含引號) 。
【 樣例輸入 1】
6 1 2
GTGGTT
【 樣例輸出 1】
1 6 1
6
【 樣例輸入 2】
2 3 2
TMG
【 樣例輸出 2】
A weird fight
【 解釋】
樣例 1 中, GT,TG,GGTT,TGGT,GTGGTT 都是“ 好” 的子串, 其中 GT 出現
了 2 次, 所以總的個數是 6。
【 資料規模與約定】
保證 m 和 g 至少有一個非 0。 不保證 m:g 是最簡比。
保證 S 中的字元只會是 M,G,T 之一。
資料可能有 3 種特殊限定之一。
限定 1: S 只出現 1 種字母。
限定 2: m*g=0
限定 3: S 中沒有字元’T’, 且所有的’M’都在所有的’G’之前。
測試點編號 t 的上界 特殊限定
1 100 1
2~5 100 無
6 2000 2
7 2000 3
8~12 2000 無
13,14 100000 2
15,16 100000 3
17~20 100000 無
保證 m,g<=109。
分析:【 特殊限定 1】
S 中的字母只有一個, 那麼任意子串對應的 m:g 都是相同的。 如果 S
中只有 M 就是 1:0, 只有 G 就是 0:1, 只有 T 就是 1:1。 因此, 如果題目中
的 m 和 g 的比不等於對應的值, 答案就是“ A weird fight” 。 如果等於對
應值, 那麼任何非空子串都是“好” 的, 因此最長的是 S[1…n], 總的個數
是 n(n+1)/2。
【 特殊限定 2】
不妨假設 m=0, 那麼如果子串包含 M 或 T, 一定不是“好” 的。 相反
地, 如果子串只包含 G, 一定是“好” 的。 所以我們可以在 S 中算出所有
連續的 G, 對應地按照特殊限定 1 的做法算出來, 統計答案也很簡單。
【 特殊限定 3】
如果 mg=0 即為限定 2, 所以考慮 m>0,g>0。 第一步先把 m:g 化簡,
兩邊同時除以 gcd(m,g)。 統計 S 中 M 的個數和 G 的個數, 那麼 S 的“好”
子串一定是 mk 個字元 M 加上 gk 個字元 G。通過統計可以算出 k 的上限,
這樣最長子串便可算出。 顯然所有“好” 的子串都不會相同, 所以最長子
串只有 1 個, 總個數就是 k 的上限。
【 t<=100】
暴力列舉子串, 暴力統計其中美國隊長和鋼鐵俠分別消滅了多少敵
人, 然後判斷兩個的比值是否等於 m:g。 注意這一步不需要 m:g 是最簡比,
也不需要求最大公約數, 否則複雜度會加上 log。 設兩人消滅的敵人數是
m1:g1, 那麼 m1:g1=m:g 當且僅當 m1g=g1*m, 這樣用一次 long long 的乘
法就可判斷。 複雜度是 O(n^3)。
【 t<=2000】
定義 Pre_m(i)表示前 i 分鐘美國隊長消滅的敵人數, Pre_g(i)表示鋼鐵
俠消滅的敵人數。 可以線上性時間內算出這兩個陣列。 這樣, 列舉子串之
後不需要暴力統計, 設列舉的子串是 S[l…r], 那麼 Pre_m( r )-Pre_m(l-1)就表
示這段時間內美國隊長消滅的敵人數, 同理 Pre_g( r )-Pre_g(l-1)表示鋼鐵俠
消滅的敵人數。 然後套用 t<=100 的方法即可, 總的複雜度是 O(n^2)。
【 t<=100000】
我們把判斷的式子寫下:
移項之後可以變成:
,
可以發現左右兩邊的式子幾乎一樣, 而且一個和 r 有關, 一個和 l-1 有關。 定義
新陣列f(i)=m * pre_g(i)-g * pre_m(i), 這樣 S[l…r]是“好” 的子串的條件就轉
化為 f( r )=f(l-1)! 注意到這是一個非常簡單的條件, 把 f 不同的值分開統計
即可。 複雜度是 O(nlogn)。 Std 的做法比較簡單, 從左到右掃一遍字串,
對不同的 f 值用 STL 中的 map 記錄第一次出現的位置, 以及出現的次數,
這樣記錄下來就可以做到 O(nlogn), 相對於第一種方法, 實現比較簡單,
速度也不慢。*
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
map <LL,int> First;
map <LL,int> num;
int n,m,g;
LL gtx[1000001];
LL mgdz[1000001];
int maxl=0,maxr=0,sum=0,ans=0,maxlen=0;
int main(){
freopen("fight.in","r",stdin);
freopen("fight.out","w",stdout);
scanf("%d %d %d",&n,&m,&g);
First[0]=num[0]=1;
for (int i=1;i<=n;i++){
char ch;
ch=getchar();
while (ch!='T'&&ch!='G'&&ch!='M') ch=getchar();
gtx[i]=gtx[i-1]+(ch=='T'||ch=='G');
mgdz[i]=mgdz[i-1]+(ch=='T'||ch=='M');
}
for (int i=1;i<=n;i++){
LL x=m*gtx[i]-g*mgdz[i];
if (num[x]){
int l=First[x];
sum+=num[x];
if (i-l+1>maxlen){
maxlen=i-l+1;
ans=1;
maxl=l,maxr=i;
}
else if (i-l+1==maxlen)ans++;
num[x]++;
}
else First[x]=i+1,num[x]=1;//因為是l-1,所以右移一位
}
if (!ans) printf("A weird fight");
else printf("%d %d %d\n%d",maxl,maxr,ans,sum);
return 0;
}
備註:暴力分很足
總結:題目給我看清楚了,變數名給我用清楚了,檔名給我寫清楚了,方位陣列給我定清楚,資料規模給我看清楚了啊啊啊