---
# System prepended metadata

title: 2020 12 月 Ten Point Round 題解
tags: [題解]

---

---
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";
    }
}

```
:::
