# 2021 6 月 Ten Point Round #11 (Div. 2) 題解
Hope you enjoyed the round!
<!---## Div.2--->
## pA Colten 所在的風原之路 (Fengyuan Road where Colten is located)
**Prepared By Colten**
其實觀察一下題目你會發現,一條道路連接著兩個圓環,也就代表有兩個圓環的道路數 $+1$,因此答案總是:$1959n + 3932m$
時間複雜度 $O(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,m;
cin >> n >> m;
for(int i=0;i<m;i++)
{
int a,b;
cin >> a >> b;
}
cout << 1959 * n + ( m * 2 ) * 1966 << "\n";
return 0;
}
```
:::
## pB 派對食物 (Party food)
**Prepared By Colten**
為了使最後每組套餐食物跟飲料的份數越多,因此我們要先求得 $GCD(x,y)$,$GCD(x,y)$ 就是我們所能分成的套餐組數,每組套餐的食物份數就是 $\frac{x}{GCD(x,y)}$,每組套餐的飲料份數就是 $\frac{y}{GCD(x,y)}$
時間複雜度:$O(\log(\min(x,y)))$
:::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;
int u = __gcd(a,b);
cout << u << " " << a / u << " " << b / u << "\n";
return 0;
}
```
:::
## pC Gunjyo 與骰子 (Gunjyo and dice)
**Prepared By Colten**
這題有一個重點是建表,如果你是在每一次輸入就去搜尋一次答案的話時間複雜度會是:$O(QN^3)$
因此我們在做任何輸入之前,就乾脆把 $1\sim100^3$ 的答案都先建好,花費 $N^3$ 的時間,接下來每組對答案的
詢問時間複雜度都只需要 $O(1)$,因此整體下來,時間複雜度 $O(N^3)$
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
int check[1000005];
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
for(int i=1;i<=100;i++)
{
for(int k=1;k<=100;k++)
{
for(int j=1;j<=100;j++)
{
check[i*k*j]++;
check[i+k+j]++;
check[i+k*j]++;
check[i*k+j]++;
}
}
}
int q;
cin >> q;
while(q--)
{
int n;
cin >> n;
cout << check[n] << "\n";
}
return 0;
}
```
:::
## pD 序列交換 (Array swap)
**Prepared By Colten**
我們先觀察,如果我們要使相乘後所得到的結果越大,那麼我們一定是要使用 $a$ 中第 $i$ 大的值就跟 $b$ 中第 $i$ 大的值相乘
那麼要使結果越小,就是使 a 中第 $i$ 大的值跟 $b$ 中第 $i$ 小的值做相乘,如此以來,相加後的總合就會是最小的了。
時間複雜度:$O(N + NlogN)$
:::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;
cin >> q;
while(q--)
{
int n;
cin >> n;
vector <int> a(n),b(n);
for(int i=0;i<n;i++)
{
cin >> a[i];
}
for(int i=0;i<n;i++)
{
cin >> b[i];
}
sort(a.begin(),a.end());
sort(b.begin(),b.end());
int ans1 = 0,ans2 = 0;
for(int i=0;i<n;i++)
{
ans1 += a[n-i-1] * b[n-i-1];
ans2 += a[i] * b[n-i-1];
}
cout << ans1 << " " << ans2 << "\n";
}
return 0;
}
```
:::
## pE 這就是我所認識的風原 (This is the Fengyuan I know)
**Prepared By Colten**
這題擴散的特性挺像 $BFS$ 的,做 $BFS$ 也可以,不過可能作法有點冗。
我們可以發現,不管對哪一個年級來說,只要每向外擴散一步,就是對那些還沒有到達的點的距離減少 $1$,因此我們
只要對每一個點與一二年級的起點,求出距離,距離越近,則代表該年級會最先到達那個點,如此一來就不用 $BFS$ 了。
感謝 $Dreamoon$ 提供此方法!
時間複雜度:$O(N^2)$
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
int mp[1005][1005];
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
for(int i=0;i<n;i++)
{
for(int k=0;k<n;k++)
{
cin >> mp[i][k];
}
}
int x1,x2,y1,y2;
cin >> x1 >> y1 >> x2 >> y2;
x1--;
x2--;
y1--;
y2--;
int ans1 = 0,ans2 = 0;
for(int i=0;i<n;i++)
{
for(int k=0;k<n;k++)
{
if( mp[i][k] != 0 )
{
int u1 = abs(x1-i)+abs(y1-k);
int u2 = abs(x2-i)+abs(y2-k);
if( u1 <= u2 ) ans1 += mp[i][k];
else ans2 += mp[i][k];
}
}
}
if( ans1 > ans2 ) cout << "Grade 1\n";
else cout << "Grade 2\n";
return 0;
}
```
:::
## pF 凱莎密碼 (Kaisa cipher)
**Prepared By Troy**
這題要根據輸入字串中某英文字母出現的次數,對某英文字母做往後偏移的動作,做法就是先讀入字串,然後去計每個字母出現的次數,再對原字串做偏移。
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define LoveRem ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define int long long
using namespace std;
int times[1010];
string dat;
signed main()
{
LoveRem
int n;
cin>>n;
while(n--){
int i;
cin>>i;
cin.ignore();
getline(cin,dat);
for(int k=0;k<i;k++){
memset(times,0,sizeof(times));
if(dat[k]!=' '){
int ch;
ch = tolower(dat[k]);
times[ch]++;
}
}//計出現次數
for(int k=0;k<i;k++){
if(dat[k]==' ') continue;
else{
if(dat[k]>='a'&&dat[k]<='z')
dat[k] = char((dat[k]+times[tolower(dat[k])]-'a')%26+'a');
if(dat[k]>='A'&&dat[k]<='Z')
dat[k] = char((dat[k]+times[tolower(dat[k])]-'A')%26+'A');
}
}
cout<<dat<<endl;
}
}
```
:::
## pG 互動題 - 風原獎勵金 (Fengyuan reward)
**Prepared By Colten**
首先我們可以先詢問序列 $S$ 的長度。
接著我們再詢問 $[1,N]$ 取得此序列中最大值的數值。
再來我們慢慢去折半詢問,詢問左邊 $[1,\frac{(L+R)}{2}]$,後,假設回報的數值 $T$ 並不是序列中的最大值,則表示最大值的位置位於右邊,我們就可以將範圍限縮再 $[\frac{L+R}{2},R]$,反之亦然。
範圍縮到最後,$L = R$ 時,就為此序列最大值所在的位置。
:::spoiler 參考解法 (C++)
``` cpp
#include <bits/stdc++.h>
//#define int long long
using namespace std;
int ask(int l,int r)
{
cout << "? " << l << ' ' << r << endl;
int ans;
cin >> ans;
return ans;
}
int ask_size()
{
int ans;
cin >> ans;
return ans;
}
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout << '@' << endl;
int n = ask_size();
int l = 1,r = n;
int mx = ask(1,n);
while(1)
{
if( r == l )
{
cout << '!' << ' ' << r << endl;
break;
}
int mid = ( l + r ) / 2;
int u1 = ask(l,mid);
if( u1 != mx )
{
l = mid + 1;
}
else
{
r = mid;
}
}
return 0;
}
```
:::
## pH 藤原千花與字串 (Chika Fujiwara and string)
**Prepared By Colten**
此題我們可以利用 set 內部元素為排序過後的特性,利用 prev 來取得上一個比新單詞還小的單詞,且這個單詞會是所有符合比新單詞還小的單詞中,字典序最大的一個。
時間複雜度:$O(NlogN)$
:::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;
cin >> q;
set <string> s = {"A"};
while(q--)
{
string input;
cin >> input;
if( s.count(input) != 0 )
{
cout << input << "\n";
continue;
}
auto it = s.insert(input).first;
string ans = *prev(it);
if( ans == "A" ) cout << -1 << "\n";
else cout << ans << "\n";
}
return 0;
}
```
:::
## pI 複製,複製,再複製!(Copy copy copy!)
**Prepared By Colten**
我們定義 $dp[i]$ 表示操作 $i$ 次,最多能得到的字串數量。
初始狀態:
$dp[0] = dp[1] = 1$
$dp[2] = 2$
轉移:
$dp[i] = max(2dp[i-2],3dp[i-3])$
$2dp[i-2]$ 就是前兩次所能取得的最大字串數量,接著做全選 + 貼上。
$3dp[i-3]$ 則是前三次所能取得的最大字串數量,接著是全選 + 貼上 + 貼上。
時間複雜度:$O(logN)$
:::spoiler 參考解法 (C++)
```cpp=
#include <bits/stdc++.h>
#define int long long
using namespace std;
int dp[60];
signed main(void)
{
ios_base::sync_with_stdio(false);
cin.tie(0);
dp[0] = dp[1] = 1;
dp[2] = 2;
for(int i=3;i<60;i++)
{
dp[i] = max(dp[i-2]*2,dp[i-3]*3);
}
int q;
cin >> q;
while(q--)
{
int n;
cin >> n;
int ans = 0;
while( dp[ans] < n ) ans++;
cout << ans << "\n";
}
return 0;
}
```
:::
## pJ 道不相同 (Different tracks)
**Prepared By Fysty**
將兩個時間點的路線圖看成兩個圖 $G,H$。
可以利用 dfs 或是 dsu 找出一個圖中的所有連通塊。
令 $i$ 在 $G,H$ 所屬的連通塊為 $g_i,h_i$。
原題就變成對於每個 $i$,有多少個 $j$ 滿足 $g_i=g_j$ 且 $h_i=h_j$。
這可以利用 stl::map 實作。
總複雜度 $O(m+k+n\log n)$
:::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 rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define pb push_back
#define F first
#define S second
const int MOD=1e9+7;
const ll INF=2e18;
const int N=2e5+5;
map<pll,ll> cnt;
vector<ll> ed[2][200005];
ll vis[2][200005],clr[2][200005],c=1;
void dfs(ll n,ll x)
{
vis[x][n]=1;
clr[x][n]=c;
for(ll u:ed[x][n]) if(!vis[x][u]) dfs(u,x);
}
int main()
{
MottoHayaku
ll n,m,k;
cin>>n>>m>>k;
rep(i,m)
{
ll a,b;
cin>>a>>b;
ed[0][a].pb(b);
ed[0][b].pb(a);
}
rep(i,k)
{
ll c,d;
cin>>c>>d;
ed[1][c].pb(d);
ed[1][d].pb(c);
}
rep(j,2)
{
rep1(i,n)
{
if(!vis[j][i])
{
dfs(i,j);
c++;
}
}
}
rep1(i,n) cnt[{clr[0][i],clr[1][i]}]++;
rep1(i,n) cout<<cnt[{clr[0][i],clr[1][i]}]<<" ";
}
```
:::