A. Shovels and Swords
貪心。每次盡可能取較多那一邊即可。
寫法上可以加速,\((2,1),(1,2)\)這種可以看作\((3,3)\),只取\((1,2)\)這種解個方程即可。
代碼如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/11 22:36:15
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int a, b; cin >> a >> b;
if (a > b) swap(a, b);
int x = min(b / 2, min(a, b - a));
b -= 2 * x;
a -= x;
int ans = x;
int c = min(a, b);
ans += c / 3 * 2;
a -= c / 3 * 3;
b -= c / 3 * 3;
if (a > b) swap(a, b);
for (int i = 3; i >= 0; i--) {
if (i <= a && 2 * i <= b) {
ans += i;
break;
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
B. Shuffle
按題意模擬即可。
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/11 22:44:42
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define l first
#define r second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n, m, x;
cin >> n >> x >> m;
vector <pii> a(m);
for (int i = 0; i < m; i++) {
int l, r; cin >> l >> r;
a[i] = MP(l, r);
}
int Min = x, Max = x;
for (int i = 0; i < m; i++) {
if (min(a[i].r, Max) >= max(a[i].l, Min)) {
Min = min(a[i].l, Min);
Max = max(a[i].r, Max);
}
}
cout << Max - Min + 1 << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
C. Palindromic Paths
找出矩陣所有對稱的斜線就行。代碼中有些細節,邊界情況要注意一下。
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/11 22:55:58
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n, m; cin >> n >> m;
vector <vector <int>> a(n, vector <int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
}
}
int t = n + m - 1;
int d = t / 2;
int ans = 0;
for (int k = 0; k < d; k++) {
vector <vector <int>> cnt(2, vector <int>(2));
int i = 0, j = k;
if (j >= m) {
i += j - m + 1;
j = m - 1;
}
while (j >= 0 && i < n) {
++cnt[0][a[i][j]];
++i, --j;
}
i = n - 1, j = m - 1 - k;
if (j < 0) {
i += j;
j = 0;
}
while (j < m && i >= 0) {
++cnt[1][a[i][j]];
--i, ++j;
}
ans += min(cnt[0][0] + cnt[1][0], cnt[0][1] + cnt[1][1]);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
D. Two Divisors
令\(a_i=p_1^{q_1}*p_2^{q_2}*\cdots *p_k^{q_k}\)構造\(d_1=p_1^{q_1},d_2=\frac{a_i}{d_1}\)就行。
因為有這個式子:
- 若\(x,y\)互質,則\(gcd(x+y,xy)=1\)。
證明的話視頻里面有,主要就是用到兩個關於\(gcd\)的性質:
- \(gcd(a,b)=gcd(a+b,b)\);
- 若\(gcd(a,c)=1\),則\(gcd(a,bc)=gcd(a,b)\)。
代碼如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/11 23:23:11
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5e5 + 5, M = 2e7 + 5;
int n;
int cnt;
int p[M];
bool chk[M];
void init() {
for(int i = 2; i < M; i++) {
if(!chk[i]) p[++cnt] = i;
for(int j = 1; j <= cnt && 1ll * i * p[j] < M; j++) {
chk[i * p[j]] = 1;
if (i % p[j] == 0) {
break;
}
}
}
}
pii ans[N];
void run() {
cin >> n;
for (int i = 1; i <= n; i++) {
int x; cin >> x;
int tmp = x;
vector <int> v;
for (int j = 1; j <= cnt; j++) {
if (1ll * p[j] * p[j] > x) {
break;
}
if (x % p[j] == 0) {
int t = 1;
while (x % p[j] == 0) {
x /= p[j];
t *= p[j];
}
v.push_back(t);
}
}
if (x > 1) {
v.push_back(x);
}
if (sz(v) < 2) {
v = {-1, -1};
} else {
v = {v[0], tmp / v[0]};
}
ans[i] = MP(v[0], v[1]);
}
for (int i = 1; i <= n; i++) {
cout << ans[i].fi << " \n"[i == n];
}
for (int i = 1; i <= n; i++) {
cout << ans[i].se << " \n"[i == n];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
init();
run();
return 0;
}
E. Two Arrays
首先判掉不合法的情況,若對於\(a_i=b_k\)且此時\(a_i\)為最后一次出現,若\(min\{a_{i+1},\cdots,a_n\}<a_i\),那么就不合法。注意開頭\(a_1,\cdots,a_j,a_j\)為\(b_1\)的最后一次出現這段要特判。
之后對於\(1,...,m-1\)每一個區間找到盡量靠左邊的右端點,那么很容易得知右端點的取值范圍,只要后面的取值合法就行,之后把所有的這些區間乘起來就行。
上述過程可以二分、單調棧、模擬等多種方法解決。
還有一種比較巧妙的做法,就是維護序列\(a\)的后綴,所有后綴最小值相等的區間天然形成了一段合法的移動區間,我們對於每個\(b_i\)把個數乘起來就行。注意一下判斷不合法的情況,前面說的在實現過程中乘起來直接等於\(0\),不用特殊判斷;但前面那一段無法確定,加上\(suf_1\not ={b_1}\)就行,這里大了小了都不滿足條件。
細節見代碼:
solution1
/*
* Author: heyuhhh
* Created Time: 2020/6/12 9:28:53
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 998244353;
void run() {
int n, m; cin >> n >> m;
vector <int> a(n), b(m);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < m; i++) {
cin >> b[i];
}
vector <int> r(m, -1);
for (int i = n - 1, j = m - 1; i >= 0; i--) {
if (j >= 0 && a[i] == b[j]) {
r[j] = i;
--j;
}
}
if (r[0] == -1) {
cout << 0 << '\n';
return;
}
r.push_back(n);
for (int i = 0; i < r[0]; i++) {
if (a[i] < b[0]) {
cout << 0 << '\n';
return;
}
}
for (int j = 0; j < m; j++) {
for (int i = r[j] + 1; i < r[j + 1]; i++) {
if (a[i] < b[j]) {
cout << 0 << '\n';
return;
}
}
}
int ans = 1;
for (int i = 0; i < m - 1; i++) {
int j = r[i + 1];
while (j > r[i] && a[j] >= b[i + 1]) {
--j;
}
ans = 1ll * ans * (r[i + 1] - j) % MOD;
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
solution2
/*
* Author: heyuhhh
* Created Time: 2020/6/12 10:09:52
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 998244353;
void run() {
int n, m; cin >> n >> m;
vector <int> a(n), b(m);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < m; i++) {
cin >> b[i];
}
vector <int> suf(n);
suf[n - 1] = a[n - 1];
for (int i = n - 2; i >= 0; i--) {
suf[i] = min(a[i], suf[i + 1]);
}
if (suf[0] != b[0]) {
cout << 0 << '\n';
return;
}
map <int, int> mp;
for (int i = 0; i < n; i++) {
++mp[suf[i]];
}
int ans = 1;
for (int i = 1; i < m; i++) {
ans = 1ll * ans * mp[b[i]] % MOD;
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
F. Jog Around The Graph
題意:
給定一個\(n\)個點,\(m\)條邊的無向帶權圖。
給定\(q\),問\(1\leq i\leq q\),從\(1\)出發經過\(i\)條邊的最長路徑為多少,記為\(L(i)\),可以經過重復的邊。
輸出\(\displaystyle\sum_{i=1}^qL(i)\)。
\(n,m\leq 2000,q\leq 10^9\)。
思路:
比較顯然的一點是當\(q\)比較大時,我們顯然的走法是在一條邊上來回走。所以我們可以考慮分情況考慮:
- \(i\leq m\),我們可以直接暴力\(dp\)進行計算,類似於bellman-ford算法枚舉邊進行更新求最長路即可;
- \(i> m\),顯然后面的若干邊反復橫跳,主要處理這種情況。
將式子寫出來,記\(f_i(e,k)\)為我們通過\(k\)條邊到\(e_u\)或者\(e_v\),之后\(i-k\)條邊反復橫跳,那么:
- \(\displaystyle f_i(e,k)=max(d[u][k],d[v][k])+(i-k)\cdot w(e)\)
顯然一條邊的貢獻為\(\displaystyle f_i(e)=max_{k=1}^{n-1}\{f_i(e,k)\}\),那么最終答案\(ans_i=max_{e}f_i(e)\)。
那么現在處理\(f_i(e,k)\),我們可以寫為:
- \(\displaystyle f_i(e,k)=max(d[u][k],d[v][k])-k\cdot w(e)+i\cdot w(e)\)
可以看作一條直線,斜率為\(w(e)\),縱截距為\(max(d[u][k],d[v][k])-k\cdot w(e)\)。顯然\(f_i(e)\)只可能縱截距越大越優。
所以現在每個\(f_e(i)\)因為邊不同為若干直線,對於每個\(i\)我們只用取最上端的即可。
一般這種貌似求一個凸包就行,但這個題因為數據范圍不是很大,我們可以直接暴力枚舉,枚舉每一條邊在最上面所在的區間\([l,r]\),然后可以直接快速計算。
假設現在枚舉\(i\),考慮另外一個\(j\),現在有直線\(y_i=w(i)\cdot i+b_i,y_jw(j)\cdot j+b_j\),我們令\(y_i>y_j\)即可確定出一段區間。這個區間上下限會不斷縮小,如果最終合法直接計算即可。
細節見代碼,主要是思路,這種線段取最優的題在圖論中貌似還是挺常見的。
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/17 18:34:51
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2000 + 5, MOD = 1e9 + 7;
int n, m;
ll q;
int u[N], v[N], w[N];
ll dp[2][N], b[N];
int ans;
void run() {
cin >> n >> m >> q;
for (int i = 1; i <= m; i++) {
cin >> u[i] >> v[i] >> w[i];
}
memset(dp, -INF, sizeof(dp));
memset(b, -INF, sizeof(b));
dp[0][1] = 0;
int ans = 0;
for (int i = 1; i < n; i++) {
for (int j = 1; j <= m; j++) {
dp[i & 1][u[j]] = max(dp[i & 1][u[j]], dp[(i - 1) & 1][v[j]] + w[j]);
dp[i & 1][v[j]] = max(dp[i & 1][v[j]], dp[(i - 1) & 1][u[j]] + w[j]);
}
for (int j = 1; j <= m; j++) {
b[j] = max(b[j], max(dp[i & 1][u[j]], dp[i & 1][v[j]]) - 1ll * i * w[j]);
}
ans = (ans + *max_element(dp[i & 1] + 1, dp[i & 1] + n + 1)) % MOD;
}
for (int i = 1; i <= m; i++) {
ll l = n, r = q;
for (int j = 1; l <= r && j <= m; j++) if (i != j) {
ll K = w[i] - w[j];
ll B = b[j] - b[i];
if (K > 0) {
l = max(l, B / K + 1);
} else if (K < 0) {
r = min(r, B / K);
} else {
if (!(B < 0 || (B == 0 && j > i))) {
r = -1;
}
}
}
if (l <= r) {
ans = ((ll)ans + (l + r) * (r - l + 1) / 2 % MOD * w[i] % MOD + b[i] * (r - l + 1) % MOD) % MOD;
if (ans < 0) ans += MOD;
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
G. Construct the String
題意:
給定\(s,t\)串。問最少刪除幾個字符使得\(f(s)=t\)。
\(f\)作用於\(s\)就類似於一個棧,依次考慮\(s_i\),若\(s_i\not ={"."}\),那么直接將\(s_i\)拼接在答案串后面;否則刪除后面一個字符,沒有就不刪除。
\(n,m\leq 10000\)。
思路:
顯然這種題考慮\(dp\),因為\(n,m\)不算很大,並且時限比較寬松,所以可以考慮\(O(nm)\)的做法,只要常數不太大就行。
那么定義\(dp[i][j]\)表示\(s\)匹配到了\(i\),\(t\)匹配到了\(j\)的最小刪除個數。
顯然我們要考慮幾種情況:
- 直接匹配,向\(dp[i+1][j+1]\)轉移;
- 刪除\(s_i\),向\(dp[i+1][j]\)轉移;
- 保留字符,可能會有兩種情況,一種是向\(dp[i+1][j]\)轉移,另一種是向\(dp[i+1][j-1]\)轉移。
第三種轉移情況不好處理,因為我們可能會存在保留若干個,中間會刪除某些,並且回退的情況,直接這樣\(dp\)是無能為力的。
注意我們保留字符時,這個字符最后是一定要被刪掉的(否則就匹配,第一種考慮了),那么我們直接找到一個最近的位置滿足這段剛好能被自己刪除就行,也就是說我們往\(dp[i+next[i]][j]\)轉移。
- 為什么直接往最近轉移最優?因為后面的情況我們也能從\(dp[i+next[i]][j]\)轉移過去,所以一定不會差。
- 為什么這樣轉移是正確的?考慮剛剛說的那種情況,要在中間刪除一些,那么這種其實先刪除\(s_i\)是等價的。這時我們像\(dp[i+1][j]\)轉移即可。
所以第三種情況我們可以直接向\(nxt[i]\)轉移,並且能保證\(dp\)正確性。
這個題感覺第三種情況的轉換這里還是挺巧妙的,很多時候\(dp\)都要發現題目中隱含的信息,這樣能夠簡化\(dp\)的轉移。
代碼不難:
Code
/*
* Author: heyuhhh
* Created Time: 2020/6/18 10:42:35
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#include <functional>
#include <numeric>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << std::endl; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
template <template<typename...> class T, typename t, typename... A>
void err(const T <t> &arg, const A&... args) {
for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
#define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
string s, t;
cin >> s >> t;
int n = s.length(), m = t.length();
vector <int> nxt(n, -1);
for (int i = 0; i < n; i++) if (s[i] != '.') {
int t = 0;
for (int j = i; j < n; j++) {
if (s[j] != '.') ++t;
else --t;
if (t == 0) {
nxt[i] = j;
break;
}
}
}
vector <vector <int>> dp(n + 1, vector <int>(m + 1, INF));
dp[0][0] = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= m; j++) {
//match
if (j < m && s[i] == t[j]) {
dp[i + 1][j + 1] = min(dp[i + 1][j + 1], dp[i][j]);
}
//skip
dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1);
//forward & back
if (s[i] != '.' && nxt[i] != -1) {
dp[nxt[i] + 1][j] = min(dp[nxt[i] + 1][j], dp[i][j]);
}
}
}
cout << dp[n][m] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}