2019-2020 ICPC Asia Taipei-Hsinchu Regional Contest
===
比賽鏈結
---
https://codeforces.com/gym/102460/
比賽影片
---
{%youtube px2wM4IfgI8 %}
記分板
---

題解
---
### A. Rush Hour Puzzle
---
#### 題目摘要
有至多10台車,垂直或水平地擺放至6 $\times$ 6的平面中,並定義出口在第3列的最右邊,車子有分兩種,長度2或3的。而你的目標是在10步內將1號車(長度為2)出去,問能不能達成
#### 想法
隱式圖,把每一種車子擺列狀況當成狀態,題目可以轉換成把目標車移出去的最短路
首先,我們可以去掉最後移出的兩步,再來,觀察到車子的狀態不會太多,所以直接BFS亂做就好
:::spoiler 實作
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
#define pb push_back
#define pii pair<int, int>
#define F first
#define S second
void solve() {
vector a(6, vector<int>(6, 0));
int mx = 0;
rep (i, 0, 6) rep (j, 0, 6) {
cin >> a[i][j];
mx = max(mx, a[i][j]);
}
vector<pair<pii, pii>> car(mx + 1);
pii h = {-1, -1}, t;
rep (i, 0, 6) rep (j, 0, 6) {
if (a[i][j] == 1) {
t = {i, j};
if (h.F == -1) h = t;
}
}
if (h.F != 2 || t.F != 2) {
cout << -1 << '\n';
return;
}
if (a[2][4] == 1 && a[2][5] == 1) {
cout << 2 << '\n';
exit(0);
}
map<vector<vector<int>>, int> mp;
queue<pair<vector<vector<int>>, int>> q;
q.push({a, 0});
mp[a] = 1;
while (!q.empty()) {
auto [cur, d] = q.front();
q.pop();
auto check = [&]() -> void {
if (cur[2][4] == 1 && cur[2][5] == 1) {
cout << d + 2 << '\n';
exit(0);
}
};
check();
if (d == 8) continue;
vector<pair<pii, pii>> car(mx + 1, {{-1, -1}, {-1, -1}});
rep (i, 0, 6) rep (j, 0, 6) if (cur[i][j]) {
car[cur[i][j]].S = {i, j};
if (car[cur[i][j]].F.F == -1) car[cur[i][j]].F = {i, j};
}
d++;
rep (i, 1, mx + 1) {
auto [h, t] = car[i];
if (h.F == t.F) {
if (h.S != 0 && cur[h.F][h.S - 1] == 0) {
rep (i, h.S - 1, t.S) swap(cur[h.F][i], cur[h.F][i + 1]);
check();
if (mp[cur] == 0) {
q.push({cur, d});
mp[cur] = 1;
}
for (int i = t.S; i >= h.S; --i) swap(cur[h.F][i], cur[h.F][i - 1]);
}
if (t.S != 5 && cur[h.F][t.S + 1] == 0) {
for (int i = t.S + 1; i > h.S; --i) swap(cur[h.F][i], cur[h.F][i - 1]);
check();
if (mp[cur] == 0) {
q.push({cur, d});
mp[cur] = 1;
}
rep (i, h.S, t.S + 1) swap(cur[h.F][i], cur[h.F][i + 1]);
}
} else {
if (h.F != 0 && cur[h.F - 1][h.S] == 0) {
rep (i, h.F - 1, t.F) swap(cur[i][h.S], cur[i + 1][h.S]);
check();
if (mp[cur] == 0) {
q.push({cur, d});
mp[cur] = 1;
}
for (int i = t.F; i >= h.F; --i) swap(cur[i][h.S], cur[i - 1][h.S]);
}
if (t.F != 5 && cur[t.F + 1][h.S] == 0) {
for (int i = t.F + 1; i > h.F; --i) swap(cur[i][h.S], cur[i - 1][h.S]);
check();
if (mp[cur] == 0) {
q.push({cur, d});
mp[cur] = 1;
}
rep (i, h.F, t.F + 1) swap(cur[i][h.S], cur[i + 1][h.S]);
}
}
}
}
cout << -1 << '\n';
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
solve();
}
```
:::
### B. The Power Monitor System (待補)
---
#### 題目摘要
#### 想法
:::spoiler 實作
```cpp=
```
:::
### C. Are They All Integers?
---
#### 題目摘要
問對於一個序列,任取3項的兩項差除以第三項是否為整數
#### 想法
簽到,$N^3$爆做
:::spoiler 實作
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
int main() {
int n; cin >> n;
vector<int> a(n);
rep (i, 0, n) cin >> a[i];
bool f = 1;
rep (i, 0, n) rep (j, 0, n) rep (k, 0, n) {
if (i != j && j != k && i != k) f &= (((a[i] - a[j]) % a[k]) == 0);
}
cout << (f ? "yes" : "no");
}
```
:::
### D. Tapioka
---
#### 題目摘要
給定一坨字串s由空格分開,把所有"tapioka" 跟 "bubble" 刪掉後輸出
#### 想法
直接實作
:::spoiler 實作
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
int main() {
string b = "bubble";
string t = "tapioka";
vector<string> ans;
string s;
while(cin >> s){
if(s!=b && s!=t){
ans.push_back(s);
}
}
for(auto v : ans){
cout << v << ' ';
}
if(ans.empty()){
cout << "nothing";
}
}
```
:::
### E. The League of Sequence Designers
---
#### 題目摘要
題目給一個要算序列中序列中最大的區間和乘區間長度的問題,並給一個錯誤的程式碼:當現在累積的和<0,就放棄這些和,否則拿這個和去更新答案。而你會拿到長度下界$L$與誤差值$k$,問你能不能構出一筆序列為$n(n>=L)$使得正確答案與錯誤答案差$k$。若能,請輸出序列,否則輸出-1
#### 想法
要滿足條件,一定要一些正數和負數,但一堆正數實在是太難想了,所以就往只有一個正數的方向想
假設$x$為那一正數,那$x$就是錯誤解的答案,而$x+k$就是正解,而只要讓序列長度為$x+k$的因數就能夠出解了。另外,序列元素的大小有限制,對於長度要找出適當$x$才行
:::spoiler 實作
```cpp=
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int lim=2000;
const ll MAX=1000000;
void solve(){
ll k;
int L;
cin >> k >> L;
for(int i=max(L, 3);i<=lim;i++){
ll sum=(MAX+k)/i;
ll x=sum*i-k;
if (sum+2<=x){
ll dif=x-sum;
vector<int> ans(i, 0);
ans[0]=x;
for(int j=1;j<i;j++){
if (dif>MAX){
ans[j]=-MAX;
dif-=MAX;
}else{
ans[j]=-dif;
dif=0;
}
}
if (dif!=0) continue;
reverse(ans.begin(), ans.end());
cout << i << '\n';
for(auto x:ans){
cout << x << ' ';
}
cout << '\n';
return;
}
}
cout << -1 << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t--) solve();
}
```
:::
### F. Miss Sloane (待補)
---
#### 題目摘要
#### 想法
:::spoiler 實作
```cpp=
```
:::
### G. Optimal Selection (待補)
---
#### 題目摘要
#### 想法
:::spoiler 實作
```cpp=
```
:::
### H. Mining a
---
#### 題目摘要
給你數字$n$,輸出滿足$\frac{1}{n}=\frac{1}{a\oplus b}+\frac{1}{b}$的最大$a$
#### 想法
賽中盲猜$b=n+1$時$a$最大,但這題好像要拆成
\begin{array}
\\(a\oplus b)\times b=nb+n(a\oplus b)\\
n^2=((a\oplus b)-n)\times(b-n)
\end{array}
因此只要枚舉$n^2$的因數就能求答案了
:::spoiler 實作
```cpp=
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
ll n;
cin >> n;
ll ans=n*(n+1);
ans^=(n+1);
cout << ans << '\n';
}
int main(){
int t;
cin >> t;
while(t--) solve();
}
```
:::
### I. The Spectrum (待補)
---
#### 題目摘要
#### 想法
:::spoiler 實作
```cpp=
```
:::
### J. Automatic Control Machine
---
#### 題目摘要
給你$m$個長度為$n$的01序列,問能不能選一些序列使它們全or起來後的序列所有元素為1。若能,輸出選的最少個數,否則輸出-1
#### 想法
$2^m$枚舉,用bitset來確認有沒有全部覆蓋
:::spoiler 實作
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
void solve() {
int n, m; cin >> n >> m;
vector<bitset<505>> bst(m);
rep (i, 0, m) rep (j, 0, n) {
char c; cin >> c;
bst[i][j] = c - '0';
}
// bitset<505> check;
// rep (i, 0, n) check[i] = 1;
int ans = m + 1;
rep (bit, 0, 1 << m) {
bitset<505> hehe;
rep (i, 0, m) if (bit >> i & 1) hehe |= bst[i];
if (hehe.count() == n) ans = min(ans, __builtin_popcount(bit));
}
if (ans == m + 1) ans = -1;
cout << ans << '\n';
}
int main() {
int t; cin >> t;
while (t--) {
solve();
}
}
```
:::
### K. Length of Bundle Rope
---
#### 題目摘要
裸霍夫曼編碼。
#### 想法
簽到,用Priority_queue實作
:::spoiler 實作
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
void solve() {
int n; cin >> n;
vector<ll> a(n);
rep (i, 0, n) cin >> a[i];
priority_queue<ll, vector<ll>, greater<ll>> pq;
rep (i, 0, n) pq.push(a[i]);
ll ans = 0;
while (pq.size() > 1) {
ll a = pq.top();
pq.pop();
ll b = pq.top();
pq.pop();
ans += a + b;
pq.push(a + b);
}
cout << ans << '\n';
}
int main() {
int t; cin >> t;
while (t--) {
solve();
}
}
```
:::
### L. Largest Quadrilateral
---
#### 題目大綱
給你二維座標上的一堆點(有重點),求最大四邊形(若選的四個點讓四邊形退化成三角形也是合法的)
#### 想法
賽中想成枚舉四邊形的一個點,將與其他點的向量拿出來排序,考慮到兩向量在固定象限中,所夾的面積具單調性,所以能用deque去維護目前選的對角線應該拿哪條向量來外積,複雜度$O(n^2\log n)$,但被卡了QQ
而正解只要枚舉對角線用旋轉卡尺維護兩邊的最大面積,複雜度$O(n^2)$
:::spoiler 實作
```cpp=
pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx,popcnt,sse4,abm")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
#define F first
#define S second
const int BUF_SZ = 1 << 15;
inline namespace Input {
char buf[BUF_SZ];
int pos;
int len;
char next_char() {
if (pos == len) {
pos = 0;
len = (int)fread(buf, 1, BUF_SZ, stdin);
if (!len) { return EOF; }
}
return buf[pos++];
}
ll read_int() {
ll x;
char ch;
int sgn = 1;
while (!isdigit(ch = next_char())) {
if (ch == '-') { sgn *= -1; }
}
x = ch - '0';
while (isdigit(ch = next_char())) { x = x * 10 + (ch - '0'); }
return x * sgn;
}
}// namespace Input
inline namespace Output {
char buf[BUF_SZ];
int pos;
void flush_out() {
fwrite(buf, 1, pos, stdout);
pos = 0;
}
void write_char(char c) {
if (pos == BUF_SZ) { flush_out(); }
buf[pos++] = c;
}
void write_int(ll x) {
static char num_buf[100];
if (x < 0) {
write_char('-');
x *= -1;
}
int len = 0;
for (; x >= 10; x /= 10) { num_buf[len++] = (char)('0' + (x % 10)); }
write_char((char)('0' + x));
while (len) { write_char(num_buf[--len]); }
}
// auto-flush output when program exits
void init_output() { assert(atexit(flush_out) == 0); }
}// namespace Output
inline pii operator-(pii x, pii y){return {x.F-y.F, x.S-y.S};}
inline ll dot(pii x, pii y){return x.F*y.F+x.S*y.S;}
inline ll cross(pii x, pii y){return x.F*y.S-x.S*y.F;}
inline ll area(pii x, pii y, pii z){return abs(cross(x-z, y-z));}
void output(ll x){
write_int((x>>1));
if (x&1) write_char('.'), write_char('5');
write_char('\n');
}
int n;
inline int nex(int x, int d=1){
x+=d;
if (x>=n) x-=n;
return x;
}
void solve(){
n=read_int();
vector<pii> pos(n);
for(auto &[x, y]:pos){
x=read_int();
y=read_int();
}
sort(pos.begin(), pos.end());
vector<pii> hull;
for(int _=0;_<2;_++){
for(int i=0;i<n;i++){
while(hull.size()>=2&&cross(pos[i]-hull.end()[-2], hull.back()-hull.end()[-2])<0) hull.pop_back();
hull.push_back(pos[i]);
}
hull.pop_back();
reverse(pos.begin(), pos.end());
}
if (hull.size()<3) cout << 0 << '\n';
else if (hull.size()==3){
ll ans=0;
ll A=area(hull[1], hull[2], hull[0]);
for(int i=0;i<n;i++) if (pos[i]!=hull[0]&&pos[i]!=hull[1]&&pos[i]!=hull[2]) ans=max(ans, A-min({area(hull[0], hull[1], pos[i]), area(hull[1], hull[2], pos[i]), area(hull[0], hull[2], pos[i])}));
output(ans);
}else{
ll ans=0;
n=hull.size();
for(int i=0;i<n;i++){
int p1=nex(i);
int p2=nex(i, 3);
for(int j=nex(i, 2);nex(j)!=i;j=nex(j)){
while(nex(p1)!=j&&area(hull[i], hull[j], hull[nex(p1)])>area(hull[i], hull[j], hull[p1])) p1=nex(p1);
while(nex(p2)!=i&&area(hull[i], hull[j], hull[nex(p2)])>area(hull[i], hull[j], hull[p2])) p2=nex(p2);
ans=max(ans, area(hull[i], hull[j], hull[p1])+area(hull[i], hull[j], hull[p2]));
}
}
output(ans);
}
}
int main(){
init_output();
int _t;
_t=read_int();
while(_t--) solve();
}
```
:::
### M. DivModulo (待補)
---
#### 題目大綱
定義 $dmod$ 如下 $A\ (dmod\ p)= a*p^x\ (dmod\ p) \equiv a \mod p$
給 $M,N,D$ 求 $\binom{N}{M}\ dmod\ D$ 的值
#### 想法
:::spoiler 實作
```cpp=
```
:::