---
title: 2020 12 月 Ten Point Round 題解
lang: zh-tw
tags: 題解
---
# 2020 12 月 Ten Point Round #4 題解
:::warning
<div class=display >
</div>
<div class=display>
<font size=5px color=indigo>
<strong>以下資訊由NHDK南部資訊沙漠提供</strong>
</font>
</br>
<img src="https://i.imgur.com/KLFGxoU.jpg" weight=300 height=300>
</div>
<style>
.display {
text-align:center;
}
</style>
:::
---
## Div.2
### pA 零錢兌換
**Prepared By Colten**
考點 : 基本輸入輸出
由於 $N$ 可能 $<5$ 所以最穩定的做法為不管 $N$ 為多少,全部都使用 $1$ 元的零錢
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
cout << n << " " << 0 << "\n";
return 0;
}
```
:::
### pB 三個數字比大小
**Prepared By Colten**
考點 : 條件判斷
備註 : 如果會使用自訂義 compare 的人這題的程式碼會較為精簡
可以一開始宣告三個變數去紀錄,比自己小或相等的數字有幾個
既然要找到第二大的數字,那麼就是找那三個用來紀錄的變數中第二大的
(也就是比自己小或相等的數字數量第二多的)
:::spoiler 參考解法 (C++) - if / else 版
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int a,b,c;
cin >> a >> b >> c;
int a2=0,b2=0,c2=0;
if( a % 2 == 1 && b % 2 == 0 )
{
a2++;
}
if( a % 2 == 1 && c % 2 == 0 )
{
a2++;
}
if( b % 2 == 1 && a % 2 == 0 )
{
b2++;
}
if( b % 2 == 1 && c % 2 == 0 )
{
b2++;
}
if( c % 2 == 1 && a % 2 == 0 )
{
c2++;
}
if( c % 2 == 1 && b % 2 == 0 )
{
c2++;
}
if( a % 2 == b % 2 )
{
if( a > b )
{
a2++;
}
else if( b > a )
{
b2++;
}
else
{
a2++;
b2++;
}
}
if( a % 2 == c % 2 )
{
if( a > c )
{
a2++;
}
else if( c > a )
{
c2++;
}
else
{
a2++;
c2++;
}
}
if( b % 2 == c % 2 )
{
if( b > c )
{
b2++;
}
else if( c > b )
{
c2++;
}
else
{
b2++;
c2++;
}
}
if( a2 >= b2 && a2 <= c2 )
{
cout << a << "\n";
}
else if( a2 >= c2 && a2 <= b2 )
{
cout << a << "\n";
}
else if( b2 >= a2 && b2 <= c2 )
{
cout << b << "\n";
}
else if( b2 <= a2 && b2 >= c2 )
{
cout << b << "\n";
}
else
{
cout << c << "\n";
}
return 0;
}
```
:::
### pC 矩形廣場圍欄模擬
**Prepared By Colten**
考點 : 迴圈 Loop
只要依照題目所給的要求去在該建設圍欄的地方輸出 $*$ 此題就解決了 !
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int q,n,i,k;
cin >> q;
int u = 1;
while(q--)
{
int a,b,ans = 0;
cin >> a >> b;
cout << "Case " << u << ":\n";
u++;
for(i=0;i!=b;i++)
{
for(k=0;k!=a;k++)
{
if( k == 0 || k == a - 1 )
{
cout << "*";
ans++;
}
else if( i == 0 || i == b - 1 )
{
cout << "*";
ans++;
}
else
{
cout << " ";
}
}
cout << "\n";
}
cout << "Use " << ans << " fences\n";
}
return 0;
}
```
:::
### pD 社團小遊戲
**Prepared By Colten**
考點 : 條件判斷 & 字元字串
此題如果單純使用整數去判斷的話可以取得 $80$ 分
不過如果要取得剩下的 $20$ 分必須使用 **字串** 來解決問題
$3$ 的倍數的判斷方法為該數每位的數字相加可整除 $3$ 則為 $3$ 的倍數
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
string a;
cin >> a;
int i,k;
int total = 0;
for(i=0;i<a.size();i++)
{
total += (a[i]-'0');
}
if( total % 3 == 0 )
{
cout << "Win\n";
}
else
{
cout << "Lose\n";
}
return 0;
}
```
:::
### pE 千萬伏特的分組
**Prepared By Colten**
考點 : 實作
題目想要進行分組且找到最小的 $RUB$ 且 $(RUB >= 0)$
我們可以先進行排序後,搜尋相鄰的兩個數字之間最小的差 $|a_i-a_{i+1}|$
最小的差即為最小的 $RUB$
參考編組如下 :
我們可以使第一組編成 $(a_1,a_2,a_3,...,a_{i-3},a_{i-2},a_{i+1})$ 此時因排序此組最大值為 $a_{i+1}$
第二組編成 $(a_{i},a_{i+2},a_{i+3},a_{i+4},...,a_n)$ 此組最小值為 $a_{i}$
$RUB = a_{i+1} - a_{i}$
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int q,n,i,k;
cin >> q;
while(q--)
{
cin >> n;
vector <int> e;
for(i=0;i<n;i++)
{
int input;
cin >> input;
e.emplace_back(input);
}
sort(e.begin(),e.end());
int mn = 1e9;
for(i=1;i<n;i++)
{
int check = e[i] - e[i-1];
mn = min(mn,check);
}
cout << mn << "\n";
}
return 0;
}
```
:::
### pF 風原特色圓環 Treffic Circle - II
**Prepared By Colten**
考點 : Greedy
只要兩種不同的圓環之間選兩個圓環蓋一條路($100$ 元)剩下的圓環都跟自己同種類的蓋道路 ($0$ 元) 這樣假如當兩種種類的圓環同時存在時最少花費必定為 $100$ 元,只存在單種種類的圓環則為 $0$ 元
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int a,b;
cin >> a >> b;
cout << ( a == 0 || b == 0 ? 0 : 100 ) << "\n";
return 0;
}
```
:::
### pG 走廊折返跑
**Prepared By Colten**
**由於題目有所爭議,賽中決定刪除此題並延長比賽 1 小時**
### pH 風原評測系統
**Prepared By Colten**
考點 : 實作 & 字元字串
此題只要照著題目意思去進行模擬即可
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
string name;
getline(cin,name);
int n,i,k;
cin >> n;
int time;
cin >> time;
cout << name << "\n";
for(i=1;i<=n;i++)
{
string a,b;
int usetime;
cin.ignore();
getline(cin,a);
getline(cin,b);
cin >> usetime;
cout << "Test " << i << ": ";
if( usetime > time )
{
cout << "Time Limit Exceeded\n";
}
else
{
if( a == b )
{
cout << "Accepted\n";
}
else
{
cout << "Wrong Answer\n";
}
}
}
return 0;
}
```
:::
## Div.1
### pA
**同 Div.2 pG**
### pB 寶石
**Prepared By Fysty**
第一批寶石融合完之後,剩下的寶石純度都非負,也就是說假設第$i$批寶石純度最高是$M_i$,則有$M_1\ge M_2\ge...$,所以答案直接取第一次融合完的最高純度就好,換句話說,答案就是$$max_{1\le i\le n}{a_i}-min_{1\le i\le n}{a_i}$$
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
#define MottoHayaku ios::sync_with_stdio(false);cin.tie(0);
#define pb push_back
#define mp make_pair
#define F first
#define S second
const ll INF=2e18;
int main()
{
MottoHayaku
ll t;
cin>>t;
while(t--)
{
ll n,mn=INF,mx=-INF;
cin>>n;
for(int i=0;i<n;i++)
{
ll k;
cin>>k;
mn=min(mn,k);
mx=max(mx,k);
}
cout<<mx-mn<<"\n";
}
}
```
:::
### pC
**同 Div.2 pH**
### pD 聖誕節禮物
**Prepared By Colten**
考點 : 動態規劃
在選禮物的規則中我們可以推出轉移式
**$dp[i]$ 為考慮 $(a_1...a_i)$ 的最大總合**
$dp[i] = max(dp[i-2]+a[i],dp[i-1])$
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a[100005],dp[100005];
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int q,n,i,k;
cin >> q;
while(q--)
{
cin >> n;
memset(dp,0,sizeof dp);
memset(a,0,sizeof a);
for(i=1;i<=n;i++) cin >> a[i];
dp[0] = 0;
dp[1] = a[1];
for(i=2;i<=n;i++)
{
dp[i] = max(dp[i-2] + a[i],dp[i-1]);
}
cout << dp[n] << "\n";
}
return 0;
}
```
:::
### pE 貼紙
**Prepared By Fysty**
令$S=\sum a_i$。
假設有$x$個橫的$y$個直的($x+y=m,0\le x\le n,0\le y\le n$)。
若$x$個橫的是放在第$i_1,i_2,...,i_x$行,$y$個直的是放在第$j_1,j_2,...,j_y$列,令$A=\sum a_{i_k},B=\sum a_{j_k}$則$W=nS-xB-yA+AB$,
整理過後變成$W=nS+(A-x)(B-y)-xy$。
固定$x,y$時,因為$A\ge x,B\ge y$,所以$A,B$越大越好,故我們可以greedy取$a$裡面前$x$大的和前$y$大的和,事前sort完算前綴和就可以$O(1)$算出來了。
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=LLONG_MAX;
#define MottoHayaku ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,n) for(ll i=0;i<n;i++)
#define rep1(i,n) for(ll i=1;i<=n;i++)
int main()
{
MottoHayaku
ll t;
cin>>t;
while(t--)
{
ll n,m,ans=-INF;
cin>>n>>m;
vector<ll> a(n),sum(n+1,0);
rep(i,n) cin>>a[i];
sort(a.begin(),a.end(),greater<ll>());
rep1(i,n) sum[i]=sum[i-1]+a[i-1];
rep(i,m+1)
{
if(i>n||m-i>n) continue;
ans=max(ans,sum[n]*m+(sum[i]-i)*(sum[m-i]-m+i)-i*(m-i));
}
cout<<ans<<"\n";
}
}
```
:::
### pF 菜月昴與XOR
**Prepared By Fysty**
透過觀察我們可以發現:$$f(n)=\left\{\begin{array}{ll}
n, & \mbox{if $n\equiv 0\mod 4$} \\
1, & \mbox{if $n\equiv 1\mod 4$} \\
n+1, & \mbox{if $n\equiv 2\mod 4$} \\
0, & \mbox{if $n\equiv 3\mod 4$}
\end{array} \right.$$
所以除了$x=0,1$的時候答案會是$\sum 4k+3,\sum4k+1$以外,
如果$x\equiv0\mod4$,那答案不是$0$就是$x$。
如果$x\equiv3\mod4$,那答案不是$0$就是$x-1$。
而其他全部都是$0$。
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
#define MottoHayaku ios::sync_with_stdio(false);cin.tie(0);
#define pb push_back
#define mp make_pair
#define F first
#define S second
int main()
{
MottoHayaku
ll t;
cin>>t;
while(t--)
{
ll x,n;
cin>>x>>n;
if(x==0)
{
ll k=n/4*4+3;
if(k>n) k-=4;
cout<<(k+3)*((k-3)/4+1)/2<<"\n";
}
else if(x==1)
{
ll k=n/4*4+1;
if(k>n) k-=4;
cout<<(k+1)*((k-1)/4+1)/2<<"\n";
}
else if(x%4==0&&x<=n) cout<<x<<"\n";
else if(x%4==3&&x-1<=n) cout<<x-1<<"\n";
else cout<<"0\n";
}
}
```
:::
### pG 打擊成績
**Prepared By amano_hina**
首先,如果打擊率是0或1,答案分別是"0 1"和"1 1"
再來,如果打擊率並非以上兩種,則可由四捨五入的定義列出一條類似以下形式的不等式
$$?_{1}\le \frac{ans1}{ans2} < ?_{2}$$
之後再用輾轉相除法就可以了喔
注意在輾轉相除法時不等號的位置
時間複雜度$O(t\log M)$ $M$為小數點後面那一串所表示的數字
:::spoiler 參考解法 (C++)
```cpp=
#include<bits/stdc++.h>
using namespace std;
long long x=0,ans1,ans2,ans2too,t,a[20],n;
char c[20];
void findmin(long long x1,long long x2,long long y1,long long y2,long long way)
{
if(way==2)
{
long long r;
if(y2==0) r=LONG_LONG_MAX;
else
{
r=y1/y2;
if(y1%y2==0) r--;
}
long long l=x1/x2;
if(x1%x2==0) l--;
if(r!=l)
{
ans1=l+1;
ans2=1;
ans2too=ans2;
return;
}
else
{
findmin(y2,y1-l*y2,x2,x1-l*x2,1);
ans2too=ans2;
ans2=ans1;
ans1=ans2*l+ans2too;
ans2too=ans2;
return;
}
}
else
{
long long r=y1/y2;
long long l=x1/x2;
if(r!=l)
{
ans1=l+1;
ans2=1;
ans2too=ans2;
return;
}
else
{
findmin(y2,y1-l*y2,x2,x1-l*x2,2);
ans2too=ans2;
ans2=ans1;
ans1=ans2*l+ans2too;
ans2too=ans2;
return;
}
}
}
int main()
{
cin.tie(0);
ios_base::sync_with_stdio(0);
a[0]=1;
for(int i=1;i<=18;i++) a[i]=a[i-1]*10;
cin>>t;
while(t--)
{
x=0;
cin>>n;
for(int i=0;i<n+2;i++) cin>>c[i];
if(c[0]=='1') cout<<"1 1"<<'\n';
else
{
for(int i=2;i<n+2;i++) x=10*x+c[i]-'0';
if(x==0) cout<<"0 1"<<'\n';
else
{
findmin(a[n+1],10*x+5,a[n+1],10*x-5,1);
cout<<ans2<<" "<<ans1<<'\n';
}
}
}
}
```
:::
### pH 指尖
**Prepared By Fysty**
首先如果存在$a_i\ge i$,則答案一定是$-1$。
否則我們一定可以構造出唯一的答案。
若最終排名為$b_1,b_2,..,b_n$
考慮一個集合$S=\{1,2,...,n\}$,
我們從$b_n$構造到$b_1$,
構造$b_i$時,$S$裡面還剩$n-i+1$個數字,其中一個是$b_i$,剩下的$n-i$個數字一定是$b_1$到$b_{n-i}$,而我們希望$b_i$要恰好輸給前面$a_i$個人,所以會發現$b_i$就是$S$裡面目前第$a_i+1$大的數字。將$b_i$從$S$中刪除,然後$i:=i-1$繼續跑,直到$i=1$,如此一來就得到我們要的排列了。
比較難的是每次要求第$a_i+1$大的數,這有幾種實作方法,可以用線段樹或BIT或pbds(?),官解用的是線段樹。
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
#define MottoHayaku ios::sync_with_stdio(false);cin.tie(0);
#define pb push_back
#define F first
#define S second
const ll INF=2e18;
const int N=200005;
ll st[4*N],a[N];
void build(ll l,ll r,ll id)
{
if(l==r) st[id]=1;
else
{
ll mid=(l+r)/2;
build(l,mid,2*id+1);
build(mid+1,r,2*id+2);
st[id]=st[2*id+1]+st[2*id+2];
}
}
void update(ll l,ll r,ll x,ll id)
{
if(l==r) st[id]=0;
else
{
ll mid=(l+r)/2;
if(x<=mid) update(l,mid,x,2*id+1);
else update(mid+1,r,x,2*id+2);
st[id]=st[2*id+1]+st[2*id+2];
}
}
ll query(ll l,ll r,ll k,ll id)
{
if(l==r)
{
if(st[id]==k) return l;
else return -1;
}
else
{
ll mid=(l+r)/2;
if(st[2*id+1]>=k) return query(l,mid,k,2*id+1);
else return query(mid+1,r,k-st[2*id+1],2*id+2);
}
}
int main()
{
MottoHayaku
ll t;
cin>>t;
while(t--)
{
ll n;
bool x=0;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]>=i) x=1;
}
if(x)
{
cout<<"-1\n";
continue;
}
vector<ll> ans;
build(1,n,0);
for(int i=n;i>=1;i--)
{
ll pos=query(1,n,st[0]-a[i],0);
ans.pb(pos);
update(1,n,pos,0);
}
for(int i=ans.size()-1;i>=0;i--) cout<<ans[i]<<" ";
cout<<"\n";
}
}
```
:::