A - Alexey and Train
模擬,沒啥需要講的,按照題目寫就行了。

#include <bits/stdc++.h> #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 309; int n, a[N], b[N], tim[N]; void work() { n = read(); for(int i = 1; i <= n; i++) { a[i] = read(); b[i] = read(); } for(int i = 1; i <= n; i++) tim[i] = read(); int now = a[1] + tim[1]; for(int i = 2; i <= n; i++) { if(now + (b[i - 1] - a[i - 1] + 1) / 2 >= b[i - 1]) { now = now + (b[i - 1] - a[i - 1] + 1) / 2; } else now = b[i - 1]; now += a[i] - b[i - 1] + tim[i]; } printf("%d\n", now); } signed main() { int Case = read(); while(Case--) work(); return 0; }
B - Napoleon Cake
題意:
有一個n層的樓,對於每個\(i∈[1,n]\),有一個\(a_i\)表示從i往下數\(a_i\)個刷上了顏色,問每層樓有沒有顏色。
題解:
當然可以用線段樹,但是有\(O(n)\)的差分做法,而且代碼量少。
因為不是在線詢問,對於每個\(a_i\),在差分數組上打上標記就可以實現區間修改了,最后詢問的時候求一下前綴和就行。

#include <bits/stdc++.h> #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 2e5 + 1009; int n, a[N], f[N]; void work() { n = read(); for(int i = 0; i <= n; i++) f[i] = 0; for(int i = 1; i <= n; i++) { a[i] = read(); f[(i - a[i] + 1) > 0 ? (i - a[i] + 1) : 1] += 1; f[i + 1] -= 1; } for(int i = 1; i <= n; i++) { f[i] += f[i - 1]; if(f[i] > 0) printf("1 "); else printf("0 "); } printf("\n"); } signed main() { int Case = read(); while(Case--) work(); return 0; }
C - Going Home
題意:
給定一列數\(f\),要求找出四個不一樣的\(a,b,c,d\)使得\(f_a + f_b=f_c + f_d\),如果沒有輸出\(no\)。
\(f_i\le 2.5\times 10^6\)
題解:
發現\(f_a + f_b\)是不超過\(5\times 10^6\)的,我們可以開個桶存下所有的\(f_a + f_b\),只要一個桶里面的數量大於4,那么一定有解。
證明:
有四對數,則情況有以下兩種:
\(A\):\((x, y),(x, z), (z, y), (任意)\)這樣子最后一對一定可以跟前面配對。
\(B\):\((x, y),(x, z), (x, p), (x, q)\)雖然這四個不能匹配,但是發現都\(f_x+f_i\)都等於一個數\(t\),也就是說明\(f_y=f_z=f_p=f_q\)這樣就可以取\(f_y+f_z=f_p + f_q\)
於是,最多枚舉\(2\times 10^7\)個數,一定存在答案。
暴力枚舉就行了。
不過我考場上的做法是直接暴力,有可能被卡,謹慎參考以下代碼。

#include <bits/stdc++.h> #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 5e6 + 1009; int n, a[N]; struct node { int x, y; }; vector<node> f[N]; signed main() { n = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int l = 1; l <= n; l++) { for(int i = 1; i + l <= n; i++) { int p = a[i] + a[i + l]; f[p].push_back((node){i, i + l}); for(int k = 0; k < f[p].size(); k++) { if(f[p][k].x == i || f[p][k].y == i) continue; if(f[p][k].x == i + l || f[p][k].y == i + l) continue; printf("YES\n"); printf("%d %d %d %d\n", f[p][k].x, f[p][k].y, i, i + l); return 0; } } } printf("NO\n"); return 0; }
D - Two chandeliers
題意:
兩隊人同時循環報數,每次報數的人假如顏色不一樣就計一次貢獻,問第\(k\)次貢獻產生的時候報到了幾。
兩隊個隊伍內部沒有重復顏色。
題解:
看官方Tutorial給的是CRT,但是我沒有用CRT也水過去了。
考場上沒看見顏色各不同結果掛了,要注意審題啊
我們先交換\(n,m\)使得\(m\le n\),這樣之后我們可以考慮假如\(b\)數組第\(1\)個和\(a\)數組第\(i\)個同時開始報數,\(b\)報回到\(1\)產生幾個貢獻。
這是可以\(O(n)\)處理出來的,方法如下。
我們把\(b\)數組里面的數與下標進行映射,這樣可以把\(b\)數組的顏色換成\(0, 1, 2, ..., m - 1\),然后考慮\(a\)數組內部的貢獻。
\(a\)數組的第\(i\)位僅僅不會對從\(i - a[i]\)開始的匹配產生貢獻,這樣我們就可以統計出從第\(i\)個位置開始匹配,不能產生貢獻的位置個數,\(m-cnt[i]\)就是產生的貢獻個數。
這樣結束后,我們一步的跨度就是\(m\)了,但是還是會超時,因為可能\(k\)很大,而\(m\)很小。
這時候我們考慮利用最小公倍數去優化枚舉。
兩列數匹配的循環節是最小公倍數,我們只需要計算最小公倍數匹配的貢獻,然后剩下的部分暴力匹配就行了。
那么最小公倍數的匹配時間復雜度是多少呢?
最小公倍數最壞情況下為\(n\times m\),每次跨越\(m\),最多循環\(n\)步就可以求出來了。
接下來的就暴力了。

#include <bits/stdc++.h> #define int long long #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 5e5 + 1009; int n, m, k, a[N], b[N], f[N * 2]; int cnt[N], lcm; int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);} int cal(int k) { int q = 0, now = 1, p = 1, tot = 0; while(q < k) { if(q + cnt[now] >= k)break; q += cnt[now]; now = (now - 1 + m) % n + 1; tot += m; } while(q < k) { q += (a[now] != b[p]); now = now % n + 1; p = p % m + 1; tot++; } return tot; } int cal1(int k) { int tt = k / m, q = 0; for(int i = 1, now = 1; i <= tt; i++) { q += cnt[now]; now = (now - 1 + m) % n + 1; } return q; } signed main() { n = read(); m = read(); k = read(); if(n <= m) { swap(n, m); for(int i = 1; i <= m; i++) b[i] = read(); for(int i = 1; i <= n; i++) a[i] = read(); } else { for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= m; i++) b[i] = read(); } memset(f, -1, sizeof(f)); for(int i = 1; i <= m; i++) f[b[i]] = i - 1; for(int i = 1; i <= n; i++) { if(f[a[i]] != -1) { cnt[(i - f[a[i]] - 1 + n) % n + 1]++; } } for(int i = 1; i <= n; i++) cnt[i] = m - cnt[i]; int p = n / gcd(n, m) * m; int tt = cal1(p), ans = 0; ans += (k - 1) / tt * p; k = (k - 1) % tt + 1; ans += cal(k); printf("%lld\n", ans); return 0; } /* cnt[i]表示從i開始匹配,能有多少個匹配上 */