---
# System prepended metadata

title: 2020 年 10 月南區四校聯合初學者程式設計線上練習賽 題解

---

# 2020 年 10 月南區四校聯合初學者程式設計線上練習賽 題解 

Div 1 Problem J 的題解跟題敘測資等等暫時不能提供
:)

## Div.2 Problem A

### By Fysty

**考點：大數輸入**

通常遇到輸入或處理大數時，不能用int或long long存，要用string存

::: spoiler 參考作法
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define MotoHaiyaku ios::sync_with_stdio(false);cin.tie(0);
int main()
{
    MotoHaiyaku
    string s;
    cin>>s;
    cout<<s;
}

```
:::

## Div.2 Problem B 風原折價券問題

### By Colten

**考點：迴圈**

對於每種折價卷的配對最先注意的是補貼的金額須為最少

第二個是要從這些組合裡選出 500 元使用最多的方案

在賽中看到很多人都只拿了 30 分，很多人的錯誤都是

1. 500 元不是使用最多的一種方案
2. 題目敘述有說 500 元 或 200 元的使用最多 5 張

::: spoiler 參考作法
```cpp=
#include <iostream>  
  
#include <cmath>  
  
#include <algorithm>  
  
#define int long long  
  
using namespace std;  
  
signed main(void)  
{  
    int n,i,k;  
      
    int ans = 1e9;  
      
    int a,b,c;  
      
    cin >> n;  
      
    for(i=0;i<=5;i++)  
    {  
        for(k=0;k<=5;k++)  
        {  
            if(n-(i*500+k*200) >= 0)  
            {  
                if(ans >= n-(i*500+k*200))  
                {  
                    ans = n-(i*500+k*200);  
                      
                    a = i;  
                      
                    b = k;  
                      
                    c = ans;  
                }  
            }  
        }  
     }  
       
     cout << a << " " << b << " " << c << "\n";   
      
    return 0;  
}
```
:::

## Div.2 Problem C
### By Fysty
首先從$AND$和$XOR$的定義可以發現：對於任何的兩個數$x,y$，$a=x\&y$和$b=x\oplus y$的二進位表示中同一個位數不會都是$1$。
所以若輸入的$a,b$滿足$a\&b\neq0$則沒有答案，輸出$-1$。
否則注意到對於任何的兩個數$x,y$，$x+y=2(x\&y)+(x\oplus y)$
所以答案即為$2a+b$。

::: spoiler 參考作法
```cpp=
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MotoHaiyaku ios::sync_with_stdio(false);cin.tie(0);
int main()
{
    MotoHaiyaku
    ll t;
    cin>>t;
    while(t--)
    {
        ll a,b;
        cin>>a>>b;
        if(a&b) cout<<"-1\n";
        else cout<<2*a+b<<"\n";
    }
}

```
:::
## Div.2 Problem D
### By Fysty
我們定義相鄰為之間不存在其他還活著的大兔。
經過觀察可以發現若某隻大兔$A$的飢餓度小於所有大兔中最大飢餓度$M$，則$A$一定在某步中被吃掉。
所以到最後只會剩下飢餓度為$M$的所有大兔。
換句話說，這題的答案就是$M$出現的所有位置。

::: spoiler 參考作法
```cpp=
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=2*1e18;
#define MotoHaiyaku ios::sync_with_stdio(false);cin.tie(0);
ll a[200005];
int main()
{
    MotoHaiyaku
    ll t;
    cin>>t;
    while(t--)
    {
        ll n,mx=-INF;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            mx=max(mx,a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i]==mx) cout<<i<<" ";
        }
        cout<<"\n";
    }
}
```
:::

## Div.2 Problem E 電子日曆 1
### By joylintp

對於字串中的每個字元，如果該字元為數字，就將答案加上點亮該數字需要的燈管數；否則就忽略該字元。

可以將每個數字需要的燈管數存在陣列中以加快實作。

::: spoiler 參考作法
```cpp=
#include<bits/stdc++.h>
using namespace std;

signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);

    int arr[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
    string s, t;
    cin >> s >> t;

    int ans = 0;
    for (char c : s)
        if (isdigit(c))
            ans += arr[c - '0'];
    for (char c : t)
        if (isdigit(c))
            ans += arr[c - '0'];
    cout << ans << '\n';

    return 0;
}
```
:::

## Div.2 Problem F / Div. 1 Problem A 綠幹線公車的完美比例

### By Colten

**考點: 迴圈 & 實作**

對於每個查詢的 OuO 數，我們可以先利用雙重迴圈去搜每個區段間的值

加入 set 裡 當在搜尋時即利用 count 去做查詢

當 count 的值為不等於 $0$ 時，輸出 $Yes$ ，反之則輸出 $No$

::: spoiler 參考作法
```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;
		
		set <int> a;
		
		vector <int> v;
		
		while(n--)
		{
			int input;
			
			cin >> input;
			
			v.push_back(input); 
		}
		
		for(i=0;i<v.size();i++)
		{
			for(k=i+1;k<v.size();k++)
			{
				a.insert(v[i]+v[k]);
			}
		}
		
		int t;
		
		cin >> t;
		
		while(t--)
		{
			int ouo;
			
			cin >> ouo;
			
			if(a.count(ouo) != 0)
			{
				cout << "Yes\n";
			}
			else
			{
				cout << "No\n";
			}
		}
		
		a.clear();
		
		v.clear();
	}
	
	return 0;
} 
```
:::


## Div.2 Problem G / Div. 1 Problem B 電子日曆 2
### By joylintp

可以發現需要最少燈管的時間為 `2111/11/11 11:11:11`，需要 31 根燈管；最多燈管的時間為 `2888/08/08 08:08:08`，需要 91 根燈管。故當 $n < 31$ 或 $n > 91$ 時無解，輸出 `-1` 即可，可得 5 分。

枚舉 2020 年中的每一秒，計算該秒所需的燈管數，可得 35 分。

可以發現，年分的後三位數和小時、分鐘、秒鐘的個位數不管其他位置的情況為何皆可設定成 0 ~ 9 之間的任何數字。故當 $n \le 61$ 時，可以從 `2111/11/11 11:11:11` 開始，每多一個燈管就將上述的其中一位數改成多使用一個燈管的數字；而若 $n \ge 61$ 時，可以從 `2111/08/08 01:01:01` 開始，每多一個燈管就將上述其中一位數改成多使用一個燈管的數字。

::: spoiler 參考作法
```cpp=
#include<bits/stdc++.h>
using namespace std;

signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);

    int n;
    cin >> n;

    if (n < 31 || n > 91)
        cout << "-1\n";
    else
    {
        int t, idx[] = {1, 2, 3, 12, 15, 18};
        string s, arr = "174208";
        if (n > 61)
            t = n - 61, s = "2111/08/08\n01:01:01";
        else
            t = n - 31, s = "2111/11/11\n11:11:11";

        for (int i = 0; i < 6; i++)
            s[idx[i]] = arr[min(t, 5)], t -= min(t, 5);

        cout << s << '\n';
    }

    return 0;
}
```
:::

## Div.2 Problem H / Div. 1 Problem C 字母加法問題

### By Colten 

**考點: 字元 & 實作**

此題提供其中一種思考路線

1. 判斷字元是否為0~9的數字，如果是可以直接將數字接上，否則須將大寫字母轉成數字
2. 如果接數字時該數字 $>=10$ 則必須將數字做分解再將其接上
3. 最後將兩組接玩的數字相加

特別注意 : 賽中發現有人忘記開 long long 導致 WA 請記得在做此題目時要開 long long

::: spoiler 參考作法
```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,b;
	
	int i,n,k;
	
	cin >> a >> b;
	
	int x = 0,y = 0;
	
	for(i=0;i<a.size();i++)
	{
		if(a[i]>='A' && a[i] <= 'Z')
		{
		    if(a[i]>='J')
		    {
		        int u = (a[i]-'A'+1);
		        
		        vector <int> c;
		        
		        while(u!=0)
		        {
		            c.push_back(u%10);
		            
		            u /= 10;
		        }
		        
		        for(k=c.size()-1;k>=0;k--)
		        {
		            x *= 10;
		            
		            x += c[k];
		            
		        }
		    }
		    else
		    {
		        x *= 10;
		        
		        x += (a[i]-'A'+1);
		    }
		}
		else
		{
		    x *= 10;
		    
			x += (a[i]-'0');
		}
	}
	for(i=0;i<b.size();i++)
	{
		if(b[i]>='A' && b[i] <= 'Z')
		{
		    if(b[i]>='J')
		    {
		        int u = (b[i]-'A'+1);
		        
		        vector <int> c;
		        
		        while(u!=0)
		        {
		            c.push_back(u%10);
		            
		            u /= 10;
		        }
		        
		        for(k=c.size()-1;k>=0;k--)
		        {
		            y *= 10;
		            
		            y += c[k];
		            
		        }
		    }
		    else
		    {
		        y *= 10;
		        
		        y += (b[i]-'A'+1);
		    }
		}
		else
		{
		    y *= 10;
		    
			y += (b[i]-'0');
		}
	}
	
	cout << x + y << "\n";
	
	return 0;
}
```
:::

## Div.2 Problem I / Div. 1 Problem D Colten 與 貢丸 的優先客人系統

### By Colten

**考點: STL-Set**

可以將非 $-1,-2$ 的數字加進 set 集合內 對於每筆查詢

可以利用 begin,rbegin,*max_element,*min_element 等等...

去取得最大值以及最小值

特別注意 : 
1. set 必須開 multiset
2. 如果是直接把最大值或最小值 erase 的人記得要先查好剩下的還有幾個 erase 完後要 insert 回去

::: spoiler 參考作法 (element)
```cpp=
#include <iostream>  
  
#include <set>  
  
#include <algorithm>  
  
using namespace std;  
  
int main(void)  
{  
    cin.tie(0);  
      
    ios_base::sync_with_stdio(false);  
      
    multiset <long long int> myset;  
      
    long long int q,n,i,k;  
      
    cin >> q;  
      
    while(q--)  
    {  
        while(cin>>n)  
        {  
            if(n==0)  
            {  
                cout << "\n";  
                  
                myset.clear();  
                  
                break;  
            }  
            else if(n<0) 
            {
            	if(myset.size() == 0)
            	{
            		cout << -1 << " ";
            		
            		continue;
				}
            	
                if(n==-2)  
                { 
                    long long int count = 0,big= *max_element(myset.begin(),myset.end());  
                  
                    count = myset.count(big) - 1;  
                      
                    cout << big << " ";  
                      
                    myset.erase(big);  
                      
                    for(i=0;i!=count;i++)  
                    {  
                        myset.insert(big);  
                    }   
                }  
                else if(n==-1)  
                {  
                    long long int count = 0,small= *min_element(myset.begin(),myset.end());  
                  
                    count = myset.count(small) - 1;  
                      
                    cout << small << " ";  
                      
                    myset.erase(small);  
                      
                    for(i=0;i!=count;i++)  
                    {  
                        myset.insert(small);  
                    }  
                }  
            }  
            else if(n>0)  
            {  
                myset.insert(n);  
            }  
        }  
    }  
      
    return 0;  
}
```
:::

## Div.2 Problem J / Div. 1 Problem E 西北望鄉何處是 東南見月幾回圓

### By Colten

**考點: 數論 & 實作**

如果暴力解的話可能只會拿到部分的分數，這時我們就要來想想如何優化

對於 $(S_i=b_i*a_1*b_i*a_2*...*b_i*a_n)$ 我們可以將其化簡成

$S_i=(a_1+a_2+...+a_n)*b_i^n$

對於取得 $b_i^n$ 的值時我們可以使用 **快速冪** 來提升效率

這麼一來程式的執行效率就提升了很多 !

::: spoiler 參考作法
```cpp=
#include <bits/stdc++.h>

#define ll long long

using namespace std;

ll fpow(ll a,ll b,ll m)
{
    if(!b) return 1;
    ll ans=fpow(a*a%m,b/2,m);
    return (b%2?ans*a%m:ans);
}
#define MotoHaiyaku ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,n) for(ll i=0;i<n;i++)
signed main()
{
    MotoHaiyaku
    ll n,m,ans1=1,ans2=1,MOD=1e8+7;
    cin>>n>>m;
    rep(i,n+m)
    {
        ll a;
        cin>>a;
        if(i<n) ans1=ans1*a%MOD;
        else ans2=ans2*a%MOD;
    }
    cout<<fpow(ans1,m,MOD)*fpow(ans2,n,MOD)%MOD;
}
```
:::

## Div. 1 Problem F 風原資訊社分組問題

### By Colten

**考點: 數論**

在進行分組的時候要讓 $GCD>1$ 最簡單的想法應該是讓每個分組的數

相加都為偶數，這麼一來就可以確保 $GCD >= 2$

所以我們只要確保以下幾點

1. 奇數 + 奇數 = 偶數
2. 偶數 + 偶數 = 偶數

開兩個陣列先把雞樹根偶數分類出來最後在依照以上兩點去做分組

如此一來即可達成題目對分組的要求

::: spoiler 參考作法
```cpp=
#include <bits/stdc++.h>

#define int long long

using namespace std;

signed main(void)
{
	int n,i,k;
	
	cin >> n;
		
	int need = n - 1;
		
	vector <int> odd;
		
	vector <int> even;
		
	for(i=0;i<2*n;i++)
	{
		int input;
			
		cin >> input;
			
		if( input % 2 == 1 )
		{
			odd.push_back(i+1);
		}
		else
		{
			even.push_back(i+1);
		}
	}
		
	for(i=1;i<odd.size()&&need!=0;i+=2,need--)
	{
		cout << odd[i] << " " << odd[i-1] << "\n";
	}
		
	for(i=1;i<even.size()&&need!=0;i+=2,need--)
	{
		cout << even[i] << " " << even[i-1] << "\n";
	}
	
	return 0;
}
```
:::

## Div. 1 Problem G 風原飛鏢之中強大的 Qum 值

### By Colten

**考點: 思維 & 實作**

題目要求每個數之間的差不能小於 $2$

這題其實算一個梗題，提供兩種做法
1. 使用 deque 分別依序重複做 push_back 以及 push_front
2. 先輸出基數再輸出偶數 ex.(1 3 5 7 9 2 4 6 8 10)

以上兩點都可以達成題目的需求

特別注意 : 使用第一個作法時須先將 5 個數字排好再進行 push 的動作

這邊提供其中一種排法 (3 1 4 2 5)

::: spoiler 參考作法
```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--)
	{
		deque <int> a;
		
		cin >> n;
		
		a.push_back(3);
		
		a.push_back(1);
		
		a.push_back(4);
		
		a.push_back(2);
		
		a.push_back(5);
		
		for(i=6;i<=n;i++)
		{
			if( i % 2 == 0 )
			{
				a.push_front(i);
			}
			else
			{
				a.push_back(i);
			}
		}
		
		for(i=0;i<n;i++)
		{
			cout << a[i] << " ";
		}
		
		cout << "\n";
	}
	
	return 0;
} 
```
:::


## Div. 1 Problem H

本題中二進位魔咒即為 de Bruijn sequence。

令子字串 $S(i, n)$ 轉換成整數的值為 $a_i$。對於所有 $i > 1$，$a_i$ 必為 $a_{i-1}\ mod\ 2^{n-1} \times 2$ 或 $a_{i-1}\ mod\ 2^{n-1} \times 2 + 1$，其中 $a\ mod\ b$ 表示 $a$ 除以 $b$ 的餘數。

暴力枚舉並檢查排列可得約 9 分。亦可人工枚舉並輸出 $n$ 值較小的結果。

其中一種快速的構造方式為，$a_1 = 0$，對於 $i > 1$，若 $a_{i-1}\ mod\ 2^{n-1} \times 2 + 1$ 尚未出現在序列 $a$ 中，則 $a_i = a_{i-1}\ mod\ 2^{n-1} \times 2 + 1$；否則，$a_i = a_{i-1}\ mod\ 2^{n-1} \times 2$。

在判斷一數是否已出現在序列中時，若使用 `set` 等資料結構會導致 $n$ 較大時 `Time Limit Exceeded`，可得到約 73 分。使用 `bool` 陣列即可得到滿分。

:::spoiler 參考作法
```cpp=
#include<bits/stdc++.h>
using namespace std;

bool use[1 << 25];

signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);

    int n;
    cin >> n;

    for (int i = 1; i < n; i++)
        cout << 0;

    int t = 1 << n, now = 0;
    while (t--)
    {
        use[now] = true;
        cout << (now & 1);

        int tt = (now & ((1 << (n - 1)) - 1)) << 1;
        if (!use[tt + 1])
            now = tt + 1;
        else
            now = tt;
    }
    cout << '\n';

    return 0;
}
```
:::

## Div. 1 Problem I 
### By amano_hina
(working in progress)
這題很簡單吧~
先來講講要怎麼拿各個子題的分好了
子題1:題目範例 
跳過
子題2:$y$是質數 
注意到在$x$和$y$之間一定不存在一數被含有質數$y$的因子，根據一些簡單的數論知識，在這個子題中一定沒有滿足題意的解，故直接輸出$-1$即可通過這個子題
子題3:$y\le 10^{6}$
這個子題就直接把在$x$跟$y$之間的所有數看能不能整除，假設能整除就直接輸出此數和$xy$除以此數，假設跑到最後沒有任何數可整除$xy$，就輸出$-1$
最差時間複雜度為$O(yt)$
子題4:$y\le 10^{7}$
注意到

:::spoiler 參考程式碼
```cpp=


```
:::




