# 112北二區學科能力競賽
題目連結:Zerojudge(https://zerojudge.tw/),Problem m601 ~ m608
## 前情提要
a是上午的題目(3hr)
pdf: https://drive.google.com/file/d/1NXjsJSPR9pBFUljCxj3qPSG3GsIYSYR6/view?usp=sharing
b是下午的題目(2hr)
pdf: https://drive.google.com/file/d/1dkE51sJpJitSWKsT8mkdhPPOxG52CU1k/view?usp=sharing
其中6a跟3b因為太難(應該啦)所以沒放在ZJ
但上面的pdf裡面有
## 1a
太簡單了不說明
反正就是畢氏定理跟一些簡單東西
:::spoiler code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
using namespace std;
using LL = long long;
int main() {
LL n;
cin >> n;
pLL arr[n];
for(auto &i : arr) cin >> i.F >> i.S;
for(LL i = 1; i < n; i ++ ) {
LL c = arr[i-1].S, b = arr[i].F - arr[i-1].F;
double a = arr[i].S / 2.0;
if(c*c-b*b < a*a) {
cout << arr[i].F << endl;
return 0;
}
}
cout << -1 << endl;
}
```
:::
## 2a
這題顯然有很多解法
這裡提供一個比較好看的解法:
窮舉每個點`p`與其他所有點的向量
如果`p`為教練,則所有向量的方向都將不同
如果`p`為球員,則有一個向量的方向與其他的不同(如果把方向相反當成同方向的話)
如果在這之中沒有教練則為無解
並且每個`p`與其他點的向量都會是同一個方向
比賽時因為想不到好的解法,所以用了map還有很多很醜的東西
雖然過了但就是挺耗時間的
(下面有code但不建議參考)
:::spoiler code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
#define mkp make_pair
#define pb emplace_back
#define LL loli
#define loli long long
using namespace std;
template<typename T> using Vec = vector<T>;
template<typename T> using Mat = Vec<Vec<T> >;
const LL mod = 1e9+7, inf = 9e18, maxN = 0;
LL TT = 1;
vector<pLL> vec;
pLL f(pLL a) {
LL g = __gcd(a.F, a.S);
a.F /= g, a.S /= g;
if(a.F < 0) a.F = -a.F, a.S = -a.S;
if(a.F == 0) a.S = labs(a.S);
return a;
}
void solve() {
LL N;
cin >> N;
vec.resize(N);
for(auto &i : vec) cin >> i.F >> i.S;
for(int i=0; i<N; i++) {
set<pLL> st;
for(int j=0; j<N; j++) if(i != j) st.insert(f(mkp(vec[j].F-vec[i].F, vec[j].S-vec[i].S)));
if(st.size() <= 2) continue;
cout << vec[i].F << ' ' << vec[i].S << endl;
return;
}
cout << "-1 -1" << endl;
}
int main() {
// IO;
// cin >> TT;
for(int cs=0; cs<TT; cs++) {
solve();
}
}
```
:::
:::spoiler 當時比賽中寫的醜醜的code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
using namespace std;
using LL = long long;
int main() {
LL n;
cin >> n;
pLL arr[n];
for(auto &i : arr)
cin >> i.F >> i.S;
map<pLL, LL> mp;
for(LL i = 0; i < n; i ++ ) {
for(LL j = i+1; j < n; j ++ ) {
LL x = arr[j].F - arr[i].F;
LL y = arr[j].S - arr[i].S;
if(x == 0)
y = 1;
else if(y == 0)
x = 1;
else {
LL g = __gcd(x, y);
x /= g, y /= g;
if(x < 0)
x = -x, y = -y;
}
mp[{x, y}] ++ ;
}
}
if(mp.size() == 1) {
cout << -1 << " " << -1 << endl;
return 0;
}
LL cnt = -1;
pLL v = {-1, -1};
for(auto &i : mp)
if(i.S > cnt) {
cnt = i.S;
v = i.F;
}
map<pLL, LL> mp2;
for(LL i = 0; i < n; i ++ ) {
for(LL j = i+1; j < n; j ++ ) {
LL x = arr[j].F - arr[i].F;
LL y = arr[j].S - arr[i].S;
if(x == 0)
y = 1;
else if(y == 0)
x = 1;
else {
LL g = __gcd(x, y);
x /= g, y /= g;
if(x < 0)
x = -x, y = -y;
}
if(v.F != x || v.S != y) {
mp2[arr[i]] ++ ;
mp2[arr[j]] ++ ;
}
}
}
cnt = -1;
pLL ans;
for(auto &i : mp2)
if(i.S > cnt) {
cnt = i.S;
ans = i.F;
}
cout << ans.F << " " << ans.S << endl;
}
```
:::
## 3a
注意一個性質:
如果我們以`(x,y)`為`3*3`的左上角做翻轉,那`(x-1,y-1)`以左上的格子都不會被翻到
那我們就可以由左往右由上往下,窮舉`(0, 0)`到`(m-3, n-3)`為`3*3`的左上角做翻轉
如果該格為`1`則翻轉,否則不轉
由於`m*n`只到`25`,所以這樣時間是好的
最後檢查是否所有格子皆為`0`,是則輸出操作次數,否則輸出`-1`
話說我當時好像是用暴搜解的來著
:::spoiler code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
#define mkp make_pair
#define pb emplace_back
#define LL loli
#define loli long long
using namespace std;
template<typename T> using Vec = vector<T>;
template<typename T> using Mat = Vec<Vec<T> >;
const LL mod = 1e9+7, inf = 9e18, maxN = 0;
int TT = 1;
void solve() {
LL n, m;
cin >> n >> m;
vector<vector<LL> > mat(n, vector<LL>(m, 0));
for(auto &i : mat) for(auto &j : i) cin >> j;
LL cnt=0;
for(LL i=0; i<n-2; i++) {
for(LL j=0; j<m-2; j++) {
if(mat[i][j]) {
cnt ++ ;
for(LL a=0; a<3; a++) for(LL b=0; b<3; b++) mat[i+a][j+b] = 1-mat[i+a][j+b];
}
}
}
bool bln = 0;
for(auto &i : mat) for(auto &j : i) if(j) bln=1;
cout << (!bln ? cnt : -1) << endl;
}
int main() {
// IO;
// cin >> TT;
for(int cs=0; cs<TT; cs++) {
solve();
}
}
```
:::
:::spoiler 當時比賽中寫的暴搜code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
using namespace std;
using LL = long long;
int main() {
LL n;
cin >> n;
pLL arr[n];
for(auto &i : arr)
cin >> i.F >> i.S;
map<pLL, LL> mp;
for(LL i = 0; i < n; i ++ ) {
for(LL j = i+1; j < n; j ++ ) {
LL x = arr[j].F - arr[i].F;
LL y = arr[j].S - arr[i].S;
if(x == 0)
y = 1;
else if(y == 0)
x = 1;
else {
LL g = __gcd(x, y);
x /= g, y /= g;
if(x < 0)
x = -x, y = -y;
}
mp[{x, y}] ++ ;
}
}
if(mp.size() == 1) {
cout << -1 << " " << -1 << endl;
return 0;
}
LL cnt = -1;
pLL v = {-1, -1};
for(auto &i : mp)
if(i.S > cnt) {
cnt = i.S;
v = i.F;
}
map<pLL, LL> mp2;
for(LL i = 0; i < n; i ++ ) {
for(LL j = i+1; j < n; j ++ ) {
LL x = arr[j].F - arr[i].F;
LL y = arr[j].S - arr[i].S;
if(x == 0)
y = 1;
else if(y == 0)
x = 1;
else {
LL g = __gcd(x, y);
x /= g, y /= g;
if(x < 0)
x = -x, y = -y;
}
if(v.F != x || v.S != y) {
mp2[arr[i]] ++ ;
mp2[arr[j]] ++ ;
}
}
}
cnt = -1;
pLL ans;
for(auto &i : mp2)
if(i.S > cnt) {
cnt = i.S;
ans = i.F;
}
cout << ans.F << " " << ans.S << endl;
}
```
:::
## 4a
好玩題目
先判斷臘腸總數是否為`n(n+1)/2`,其中`n`為整數,如果不是則可直接輸出`-1`,否則繼續
再來,我們可以注意到`m`為`3`或`4`,仔細想想就可以發現原因
可以從最上面的打結處將此串臘腸分割成`2`條無分支的臘腸條
注意操作分割時其實就是將一陣列與另一陣列反轉後相接
再來,我們針對其中一串進行切割
靠一點點的感覺就能知道,如果我們從`n`往下判斷是否可以切成此長度
我們可以將此串切成長度為`1~n`中其中幾個不重複數字的長度
沒切到的數字即為另一條要切的長度
:::spoiler code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
#define pb emplace_back
using namespace std;
using LL = long long;
int main() {
LL m;
cin >> m;
LL k[m], sum = 0;
for(LL i = 0; i < m; i ++ )
cin >> k[i], sum += k[i];
vector<LL> mat[m];
for(LL i = 0; i < m; i ++) {
for(LL j = 0; j < k[i]; j ++ ) {
LL num;
cin >> num;
mat[i].pb(num);
}
}
LL n = 1;
for(LL k = 0; ; n ++ ) {
k += n;
if(k > sum) {
cout << -1 << endl;
return 0;
}
if(k == sum)
break;
}
LL a = k[0] + k[1], b = k[2];
if(m == 4)
b += k[3];
vector<LL> A, B;
for(LL i = k[0]-1; i >= 0; i -- )
A.pb(mat[0][i]);
for(auto &i : mat[1])
A.pb(i);
for(LL i = k[2]-1; i >= 0; i -- )
B.pb(mat[2][i]);
if(m == 4) {
for(auto &i : mat[3])
B.pb(i);
}
bool used[n + 1];
memset(used, 0, sizeof(used));
vector<LL> ans[n+1];
for(LL i = n, l = 0; i >= 1; i -- ) {
if(a >= i) {
a -= i;
used[i] = 1;
for(LL j = 0; j < i; j ++ ) {
ans[i].pb(A[l]);
l ++ ;
}
}
}
for(LL i = 1, l = 0; i <= n; i ++ ) {
if(!used[i]) {
used[i] = 1;
for(LL j = 0; j < i; j ++ ) {
ans[i].pb(B[l]);
l ++ ;
}
}
}
cout << n << endl;
for(LL i = 0; i < n; i ++ ) {
for(auto &j : ans[i+1])
cout << j << " ";
cout << endl;
}
}
```
:::
## 5a
其實這題也是滿水的
就直接從1跑到N然後做質因數分解存在陣列裡面
最後處理一下輸出格式就好
比較難的地方可能只有題目會讓你想的太難
還有就是分析複雜度的部分
因為質數有很多有關數論的證明跟複雜度要背
不熟的話可能會被題目嚇到然後不敢寫這題
時間複雜度計算:
跑N的質因數分解複雜度為$\sqrt N$
所以整體大概會是$N\sqrt N$
如果N到5e5的話$N\sqrt N$大概會到3e8
算是卡在1秒邊但因為ZJ開2秒所以可以過
同時這個複雜度是上界並且有滿多k的操作會比$k \sqrt k$ 快
所以甚至可以在1秒內完成
::: spoiler code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
#define pb emplace_back
using namespace std;
using LL = long long;
const LL maxN = 5e5+5;
int main() {
IO;
LL n;
cin >> n;
vector<LL> ans(maxN, 0);
for(LL i = 2; i <= n; i ++ ) {
LL p = i;
for(LL j = 2; j * j <= p; j ++ ) {
while(p % j == 0) {
p /= j;
ans[j] ++ ;
}
}
if(p != 1)
ans[p] ++ ;
}
LL tmp = -1, cnt = 0;
for(LL i = 0; i < maxN; i ++ ) {
if(!ans[i])
continue;
if(tmp == -1) {
tmp = ans[i];
cnt = 1;
continue;
}
if(ans[i] == tmp)
cnt ++ ;
else {
if(cnt > 1)
cout << cnt << '*';
cout << tmp << " ";
tmp = ans[i];
cnt = 1;
}
}
if(cnt > 1)
cout << cnt << '*';
cout << tmp << endl;
}
```
:::
## 6a
這題頗難,同時我不會
但應該是跟LCA有關
加上一些神奇東西
## 1b
~~我懷疑這題跟2a是出題者同時想到的~~
其實就跟2a的想法有點像,只是多了一些步驟
我們可以選三個點a, b, c然後決定其中兩條線
並用這兩條線去判斷是不是所有人都在這兩條線上
這樣解在ZJ上是95%但賽中是AC的
很神奇
::: spoiler code
:::
::: spoiler 當時比賽中寫的code(ZJ 95%)
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
using namespace std;
using LL = long long;
int main() {
LL n;
cin >> n;
pLL arr[n];
for(auto &i : arr)
cin >> i.F >> i.S;
uniform_int_distribution<LL> dis(0, n-1);
mt19937 mt(rand());
LL a[3];
pLL p, v;
while(1) {
for(LL i = 0; i < 3; i ++ ) {
a[i] = dis(mt);
}
if(a[0] == a[1] || a[0] == a[2] || a[1] == a[2])
continue;
LL x1 = arr[a[1]].F - arr[a[0]].F, y1 = arr[a[1]].S - arr[a[0]].S;
if(x1 == 0) y1 = 1;
else if(y1 == 0) x1 = 1;
else {LL g = __gcd(x1, y1); x1 /= g, y1 /= g; if(x1 < 0) x1 = -x1, y1 = -y1;}
LL x2 = arr[a[2]].F - arr[a[0]].F, y2 = arr[a[2]].S - arr[a[0]].S;
if(x2 == 0) y2 = 1;
else if(y2 == 0) x2 = 1;
else {LL g = __gcd(x2, y2); x2 /= g, y2 /= g; if(x2 < 0) x2 = -x2, y2 = -y2;}
if(x1 != x2 || y1 != y2)
continue;
p = arr[a[0]];
v = {x1, y1};
break;
}
char A = 'A', B = 'B';
for(LL i = 0; i < n; i ++ ) {
LL x1 = arr[i].F - p.F, y1 = arr[i].S - p.S;
if(x1 == 0 && y1 == 0) {
cout << A << endl;
continue;
}
if(x1 == 0) y1 = 1;
else if(y1 == 0) x1 = 1;
else {LL g = __gcd(x1, y1); x1 /= g, y1 /= g; if(x1 < 0) x1 = -x1, y1 = -y1;}
if(v.F == x1 && v.S == y1) {
cout << A << endl;
}
else {
if(i == 0)
swap(A, B);
cout << B << endl;
}
}
}
```
:::
## 2b
這題很好玩,因為不是平常會遇到的題目
感覺也是有很多神奇作法的題目
反正我賽中是拿0%,現在也還沒AC
我們知道只有10個數字要處理
那我們就可以對每個數字去找他們的特點
例:1的x軸不會動,之類的
那我們在仔細觀察
可以發現除了1以外,我們都可以利用王老先生的路徑找到整個數字的長或寬
並推出整個七段顯示器的大小跟每一線段的座標值
## 3b
這題頗難,我也不會
但滿分應該是2-SAT
喇分的話可以暴搜或dp
## 4b
這題有點像輾轉相除法的感覺
因為你可以發現當把一個分數用1去除他的時候其實是取倒數
我們只要維護兩個數字p, q表示一開始的分數是$\frac{p}{q}$
只要把整數提出來然後再把它變成倒數
一直重複以上動作即可AC
:::spoiler code
```cpp=
#include<bits/stdc++.h>
#define IO cin.tie(0) -> sync_with_stdio(0)
#define endl "\n"
#define F first
#define S second
#define pLL pair<LL, LL>
#define pb emplace_back
using namespace std;
using LL = long long;
int main() {
string str;
cin >> str;
LL pos = str.length() - str.find('.')-1;
LL a = 0, b = 1;
for(auto &i : str) {
if(i == '.')
continue;
a = a * 10 + i - '0';
}
for(LL i = 0; i < pos; i ++ )
b *= 10;
vector<LL> vec;
if(a == 0) {
cout << 0 << endl;
return 0;
}
LL g = __gcd(a, b);
a /= g, b /= g;
while(1) {
vec.pb((LL)(a/b));
a %= b;
if(!a)
break;
swap(a, b);
}
for(LL i = 0; i < vec.size(); i ++ ) {
if(i == 1)
cout << ";";
else if(i != 0)
cout << ",";
cout << vec[i];
}
cout << endl;
}
```
:::