【題解】Atcoder ABC 315, 317 === ## 315 D 每個回合會刪除至少一行或一列,因此刪除最多 $O(H+W)$ 次。每次的刪除,可以用 $O(HW)$ 的時間找到可以刪除的行列,但複雜度會爆炸。 考慮到元素種類只有 $\rho = 26$ 種,我們可以對每一行、每一列維護一個每個元素對應數量的表,如 $rowCnt[i][c]$ 代表第 $i$ 行有幾種 $c$ 元素。對於一個行、列,只要用 $O( \rho )$ 即可檢查是否可以刪除該行列。那麼每個回合的複雜度將為 $O(\rho(H+W)))$。 要注意的是,在統計完可刪除行列前,如果就移除字符,會對後續統計產生影響。 ```cpp #include <bits/stdc++.h> #include <ext/pb_ds/assoc_container.hpp> using namespace std; using namespace __gnu_pbds; typedef long long ll; typedef pair<int, int> pii; typedef vector<int> vi; #define V vector #define sz(a) ((int)a.size()) #define all(v) (v).begin(), (v).end() #define rall(v) (v).rbegin(), (v).rend() #define pb push_back #define rsz resize #define mp make_pair #define mt make_tuple #define ff first #define ss second #define FOR(i,j,k) for (int i=(j); i<=(k); i++) #define F0R(i,j,k) for (int i=(j); i<(k); i++) #define REP(i) FOR(_,1,i) #define foreach(a,x) for (auto& a: x) template<class T> bool cmin(T& a, const T& b) { return b < a ? a = b, 1 : 0; } // set a = min(a,b) template<class T> bool cmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; } // set a = max(a,b) ll cdiv(ll a, ll b) { return a/b+((a^b)>0&&a%b); } ll fdiv(ll a, ll b) { return a/b-((a^b)<0&&a%b); } #define roadroller ios::sync_with_stdio(0), cin.tie(0); #define de(x) cerr << #x << '=' << x << ", " #define dd cerr << '\n'; signed main() { int n, m; cin >> n >> m; V<string> a(n); F0R(i,0,n) cin >> a[i]; V<vi> rcnt(n, vi(128)), ccnt(m, vi(128)); V<V<vi>> rpos(n, V<vi>(128, vi())), cpos(m, V<vi>(128, vi())); F0R(i,0,n){ F0R(j,0,m){ auto c = a[i][j]; rcnt[i][c]++, ccnt[j][c]++; rpos[i][c].pb(j); cpos[j][c].pb(i); } } auto ckrow = [&](int i){ int ans = 0; FOR(j,'a','z'){ if (rcnt[i][j] > 0){ if (!ans) ans = j; else return 0; } } if (rcnt[i][ans] <= 1) return 0; return ans; }; auto ckcol = [&](int i) { int ans = 0; FOR(j,'a','z'){ if (ccnt[i][j] > 0){ if (!ans) ans = j; else return 0; } } if (ccnt[i][ans] <= 1) return 0; return ans; }; while(1){ int flag = 0; V<pair<int, char>> drow; F0R(i,0,n){ char x = ckrow(i); if (x){ drow.pb(mp(i, x)); flag = 1; } } F0R(j,0,m){ char x = ckcol(j); if (x){ flag = 1; foreach(p, cpos[j][x]){ rcnt[p][x]--; } ccnt[j][x] = 0; } } foreach(r,drow){ auto [i, x] = r; foreach(p, rpos[i][x]) ccnt[p][x]--; rcnt[i][x] = 0; } if (!flag) break; } int ans = 0; F0R(i,0,n){ FOR(c,'a','z'){ ans += rcnt[i][c] * (rcnt[i][c]>0); } } cout << ans << '\n'; } ``` ## 315 F 省略一個點位,最多可以省掉 $10^4 \sqrt2$ 的移動距離(對角線)。但隨著省略次數增加,花費會快速增長。當 $2^{c-1}$ 已經超過可以省掉的最長移動距離,那麼就省掉一定是不划算的。我們可以取最大的可能 $c$ 在 $log(10^5)$ 。 當知道 $c$ 的範圍很小,我們可以嘗試 DP。列出狀態 $dp[i][j]$ 代表現在人在 $i$,還可以跳過 $j$ 次。轉移只要枚舉這一步要跳過幾個就好了,複雜度 $O(NC^2)$。 ```cpp #include <bits/stdc++.h> #include <ext/pb_ds/assoc_container.hpp> using namespace std; using namespace __gnu_pbds; typedef long long ll; typedef pair<int, int> pii; typedef vector<int> vi; #define V vector #define sz(a) ((int)a.size()) #define all(v) (v).begin(), (v).end() #define rall(v) (v).rbegin(), (v).rend() #define pb push_back #define rsz resize #define mp make_pair #define mt make_tuple #define ff first #define ss second #define FOR(i,j,k) for (int i=(j); i<=(k); i++) #define F0R(i,j,k) for (int i=(j); i<(k); i++) #define REP(i) FOR(_,1,i) #define foreach(a,x) for (auto& a: x) template<class T> bool cmin(T& a, const T& b) { return b < a ? a = b, 1 : 0; } // set a = min(a,b) template<class T> bool cmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; } // set a = max(a,b) ll cdiv(ll a, ll b) { return a/b+((a^b)>0&&a%b); } ll fdiv(ll a, ll b) { return a/b-((a^b)<0&&a%b); } #define roadroller ios::sync_with_stdio(0), cin.tie(0); #define de(x) cerr << #x << '=' << x << ", " #define dd cerr << '\n'; #define int long long int qpow(int i, int j){ j--; if (j < 0) return 0; int ans = 1; for (; j; j>>=1, i*=i){ if (j&1) ans *= i; } return ans; } signed main(){ roadroller int n; cin >> n; V<pii> a(n+5); FOR(i,1,n) cin >> a[i].ff >> a[i].ss; int m = 31; V<V<double>> dp(n+5, V<double>(m, 1e18)); auto cost = [&](int i, int j){ return sqrt( (a[i].ff-a[j].ff)*(a[i].ff-a[j].ff) + (a[i].ss-a[j].ss)*(a[i].ss-a[j].ss) ); }; F0R(j, 0, m) dp[1][j] = 0; FOR(i,2,n){ F0R(j,0,m){ double c = 0; int src = 0; FOR(k,0,min(j, i-2)){ cmin(dp[i][j], dp[i-1-k][j-k] + cost(i-1-k, i)); } } } double ans = 1e18; F0R(i, 0, m){ ans = min(ans, dp[n][i] + qpow(2, i) ); } cout << fixed << setprecision(10) << ans << '\n'; } ``` ## 315 G 枚舉 $i$,那麼 $Bj + Ck = X-Ai$ 就可以用擴展歐幾里德解出 $j, k$。用擴展歐幾里德獲得一組 $Ax+By=C$ 的解 $x, y$,那麼 $(x + i \frac B g, y - i \frac A g),\, g=gcd(A,B)$ 也是一組解。 可以用這個方式找到所有合法的解。直接帶入不等式即可。 ```cpp #include <bits/stdc++.h> #include <ext/pb_ds/assoc_container.hpp> using namespace std; using namespace __gnu_pbds; typedef __int128 ll; typedef pair<int, int> pii; typedef vector<int> vi; #define V vector #define sz(a) ((int)a.size()) #define all(v) (v).begin(), (v).end() #define rall(v) (v).rbegin(), (v).rend() #define pb push_back #define rsz resize #define mp make_pair #define mt make_tuple #define ff first #define ss second #define FOR(i,j,k) for (int i=(j); i<=(k); i++) #define F0R(i,j,k) for (int i=(j); i<(k); i++) #define REP(i) FOR(_,1,i) #define foreach(a,x) for (auto& a: x) template<class T> bool cmin(T& a, const T& b) { return b < a ? a = b, 1 : 0; } // set a = min(a,b) template<class T> bool cmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; } // set a = max(a,b) ll ccdiv(ll a, ll b) { return a/b+((a^b)>0&&a%b); } ll ffdiv(ll a, ll b) { return a/b-((a^b)<0&&a%b); } #define roadroller ios::sync_with_stdio(0), cin.tie(0); #define de(x) cerr << #x << '=' << x << ", " #define dd cerr << '\n'; pair<ll, ll> euclid(ll A, ll B) { if (!B) return {1,0}; auto p = euclid(B,A%B); return {p.second,p.first-A/B*p.second}; } ll solve(ll a, ll b, ll c, ll n){ ll g = __gcd(a, b); if (c % g) return 0; auto [x0, y0] = euclid(a, b); ll x = c/g * x0, y = c/g * y0; ll dx = b/g, dy = a/g; ll l1 = ccdiv(1-x, dx), u1 = ffdiv(n-x, dx); ll l2 = ccdiv(y-n, dy), u2 = ffdiv(y-1, dy); return max(__int128(0), min(u1,u2) - max(l1, l2) + 1); } signed main() { long long n, a, b, c, X; cin >> n >> a >> b >> c >> X; long long ans = 0; FOR(i,1,n){ ans += solve(b, c, X - a*i, n); } cout << ans << '\n'; } ``` ## 317 F 由於該題位數很大,我們考慮用數位 DP。由於規定 XOR,因此在二進制上數位 DP。訂定狀態 $dp[n][l_1][l_2][l_3][w_1][w_2][w_3][r_1][r_2][r_3]$, $n$ 代表要決定 $Xi$ 的前 $n-1$ 位, $l_i$ 代表 $X_i$ 接下來需不需要小於 $N$ 的對應位數, $w_i$ 代表 $X_i$ 目前是否 $>0$, $r_i$ 代表 $X_i$ 接下來使得 $X_i \% A_i = r_i$。 ```cpp #include <bits/stdc++.h> #include <ext/pb_ds/assoc_container.hpp> using namespace std; using namespace __gnu_pbds; typedef long long ll; typedef pair<int, int> pii; typedef vector<int> vi; #define V vector #define sz(a) ((int)a.size()) #define all(v) (v).begin(), (v).end() #define rall(v) (v).rbegin(), (v).rend() #define pb push_back #define rsz resize #define mp make_pair #define mt make_tuple #define ff first #define ss second #define FOR(i,j,k) for (int i=(j); i<=(k); i++) #define F0R(i,j,k) for (int i=(j); i<(k); i++) #define REP(i) FOR(_,1,i) #define foreach(a,x) for (auto& a: x) template<class T> bool cmin(T& a, const T& b) { return b < a ? a = b, 1 : 0; } // set a = min(a,b) template<class T> bool cmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; } // set a = max(a,b) ll cdiv(ll a, ll b) { return a/b+((a^b)>0&&a%b); } ll fdiv(ll a, ll b) { return a/b-((a^b)<0&&a%b); } #define roadroller ios::sync_with_stdio(0), cin.tie(0); #define de(x) cerr << #x << '=' << x << ", " #define dd cerr << '\n'; #define int long long const int M = 998244353; int N, a1, a2, a3; int dp[65][2][2][2][2][2][2][15][15][15]; int f (int n, int l1, int l2, int l3, int w1, int w2, int w3, int r1, int r2, int r3) { auto &dpnow = dp[n][l1][l2][l3][w1][w2][w3][r1][r2][r3]; if (dpnow < 0){ if (n == 0) { dpnow = !(r1 || r2 || r3) && w1 && w2 && w3; return dpnow; } V<V<pii>> can(4); auto gen = [&](V<pii> &can, int l, int r, int a) { if (l){ if ( N & (1LL<<(n-1)) ) { can.pb( mp(0, r) ); can.pb( mp(1, ((r - (1LL<<(n-1)))%a+a)%a) ); } else { can.pb( mp(1, r)); } } else { can.pb( mp(0, r) ); can.pb( mp(0, ((r - (1LL<<(n-1)))%a+a)%a) ); } }; gen(can[1], l1, r1, a1); gen(can[2], l2, r2, a2); gen(can[3], l3, r3, a3); dpnow = 0; F0R(i,0,sz(can[1])){ F0R(j,0,sz(can[2])){ F0R(k,0,sz(can[3])){ if (i ^ j ^ k) continue; dpnow = (dpnow + f(n-1, can[1][i].ff, can[2][j].ff, can[3][k].ff, w1 || i, w2 || j, w3 || k, can[1][i].ss, can[2][j].ss, can[3][k].ss))%M; } } } } return dpnow; } signed main() { roadroller memset(dp, -1, sizeof(dp)); cin >> N >> a1 >> a2 >> a3; cout << f(61, 1, 1, 1, 0, 0, 0, 0, 0, 0) << '\n'; } ```