2024 (ICPC) Jiangxi Provincial Contest -- Official Contest
A. Maliang Learning Painting
題意:a + b + c
void solve(){ ll a, b, c; cin >> a >> b >> c; ll ans = a + b + c; cout << ans << '\n'; }
C. Liar
題意:n 個 數字總和是 s ,每個人說出自己得到的數字,有人可能撒謊,問最多有多少人說真話
思路:首先總和就是 s 的話,最多所有人說的都是真話,再或者假設 1~i 的人都不撒謊,只有第 n 個人撒謊
void solve(){ ll n, s; cin >> n >> s; ll sum = 0; for(int i = 1; i <= n; i ++){ ll x; cin >> x; sum += x; } if(sum == s) cout << n << '\n'; else cout << n - 1 << '\n'; }
D. Magic LCM
題意:給定 n 個數,每次可以選定兩個數,把其中一個改為 gcd(x, y),另外一個改為 lcm(x, y),求最大的數列和
思路:可以發現,透過操作,可以把二者共有的質因子的最大的冪交給一方,如果是非共有的質因子,會交給較大的一方,相當於共有質因子的較大冪和非共有質因子交給一方,共有質因子的較小冪交給另外一方,我們可以先處理 1e3 以內的質數,求出每個數的質因子,然後對共有質因子的冪進行排序,然後找出項數最多的質因子,對該質因子的所有冪排序,其它的質因子會全部轉移當這個項數最多的質因子上,同時我們要貪心的其它質因子的每一項的冪按照大和大匹配來運算,最後不要忘了除了最多項數的質因子之外其它的全部變成了1,要把它們加上
const int N = 1000010, mod = 998244353; const int Mod = 1e9 + 7, INF = 0x3f3f3f3f; bool vis[1010]; vector<ll> pri; void ola_s(ll n){ for(int i = 2; i <= n; i ++){ if(!vis[i]) pri.push_back(i); for(auto v : pri){ if(i * v > n) break; vis[i * v] = true; if(i % v == 0) break; } } } vector<ll> v[N]; set<ll> st; void get_fac(ll x){ for(auto p : pri){ if(x < p) break; if(x % p == 0){ ll t = 1; while(x % p == 0) { t *= p; x /= p; } v[p].push_back(t); st.insert(p); } } if(x > 1){ v[x].push_back(x); st.insert(x); } } void solve(){ ll n; cin >> n; vector<ll> a(n + 1); for(int i = 1; i <= n; i ++){ cin >> a[i]; get_fac(a[i]); } ll len = 0, ma = 0;; for(auto i : st){ if(v[i].size() > len) len = v[i].size(), ma = i; sort(v[i].begin(), v[i].end()); } for(auto i : st){ if(i == ma) continue; for(int j = len - 1, k = v[i].size() - 1; k >= 0; j --, k --){ v[ma][j] = v[ma][j] * v[i][k] % mod; } } ll ans = n - v[ma].size(); for(auto i : v[ma]) ans = (ans + i + mod) % mod; cout << ans << '\n'; for(auto i : st) v[i].clear(); st.clear(); } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); ola_s(1000); int t = 1; cin >> t; while(t --){ solve(); } return 0; }
G. Multiples of 5
題意:給定一個11進位制數,求它是不是 5 的倍數
思路:首先這個11進位制數非常大,肯定不能轉化成十進位制的數來求,我們手推一下 11 的沒一項冪會發現它們的最後一個數字都是 1 ,也只有這個 1 會影響答案,我們求出 11 進位制下每一位有多少個,然後加一下看是不是 5 的整數倍就行了。(又把‘A’ == 10當成'A' == 11 wa了一發)
void solve(){ ll n; cin >> n; ll ans = 0; for(int i = 1; i <= n; i ++){ ll a1, a2; char b; cin >> a1 >> b; if(b != 'A') a2 = (b - '0'); else a2 = 10; ans = (ans + a1 * a2) % 5; } if(ans == 5 || ans == 0) cout << "Yes\n"; else cout << "No\n"; }
H. Convolution
題意:給定一個n * m的矩陣 a ,求一個k * l的矩陣 b 和 a 的乘積的最小值,b 只包含(-1, 0, 1)
思路:手畫一下發現,矩陣 b 的每一項,會和矩陣 a 左上角 (i, j) ~ (n - k + i, m - l + j) 的子矩陣相乘,處理一下二維字首和,正數取1,否則取-1
void solve(){ ll n, m, k, l; cin >> n >> m >> k >> l; vector<vector<ll>> I(3010, vector<ll>(3010)); vector<vector<ll>> IQ(3010, vector<ll>(3010)); for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++){ cin >> I[i][j]; ll x = I[i][j]; IQ[i][j] = x + IQ[i - 1][j] + IQ[i][j - 1] - IQ[i - 1][j - 1]; } } auto query =[&](ll x1, ll y1, ll x2, ll y2) -> ll{ ll ans = IQ[x2][y2] - IQ[x1 - 1][y2] - IQ[x2][y1 - 1] + IQ[x1 - 1][y1 - 1]; return ans; }; ll res = 0; for(int i = 1; i <= k; i ++){ for(int j = 1; j <= l; j ++){ res += abs(query(i, j, n - k + i, m - l + j)); } } cout << res << '\n'; }
I. Neuvillette Circling
題意:給定 n 個點,所有點之間都有邊相連線,問包含 1 ~ n * (n - 1) / 2 條邊分別至少需要的半徑
思路:分類討論,兩種情況,兩個點確定一個圓,三個點確定一個圓(外接圓),然後分別求一下包含多少個點
using db = double; const db eps = 1e-6; int sign(db a) { return a < -eps ? -1 : a > eps; } //< :-1 =:0 >:1 struct Point { db x, y; Point() {} Point(db x, db y) : x(x), y(y) {} Point operator+(Point p) { return {x + p.x, y + p.y}; } Point operator-(Point p) { return {x - p.x, y - p.y}; } Point operator*(db d) { return {x * d, y * d}; } Point operator/(db d) { return {x / d, y / d}; } bool operator==(Point &p) const { return !sign(x - p.x) && !sign(y - p.y); } int toleft(Point b) { // 點線上段的方向 ll res = x * b.y - y * b.x; if (res == 0) return 0; // 直線上 else if (res > 0) return 1; // 左 return -1; // 右 } friend istream &operator>>(istream &is, Point &p) { return is >> p.x >> p.y; } friend ostream &operator<<(ostream &os, Point p) { return os << "(" << p.x << ", " << p.y << ")"; } }; struct Circle { Point o; db r; Circle() {} Circle(Point _o, db _r) : o(_o), r(_r) {} }; db dot(Point a, Point b) { return a.x * b.x + a.y * b.y; } db cross(Point a, Point b) { return a.x * b.y - a.y * b.x; } db dis1(Point a, Point b) { // 兩點距離的平方 return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); // return sqrtl((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));//long double ver } db dis2(Point a, Point b) { // 兩點距離的平方 return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); } bool ver(Point a, Point b) { // 是否垂直 return sign(dot(a, b)) == 0; } db pw(db x) { return x * x; } db fun(db a1, db a2, db a3, db b1, db b2, db b3, db c1, db c2, db c3) { return a1 * b2 * c3 + b1 * c2 * a3 + c1 * a2 * b3 - a3 * b2 * c1 - b3 * c2 * a1 - c3 * a2 * b1; } Circle three_p_get_cir(Point a, Point b, Point c) { // 求三角形的外接圓 db A = dis1(a, b), B = dis1(b, c), C = dis1(a, c); db L = (A + B + C) / 2; // 三角形半周長 db S = sqrt(L * (L - A) * (L - B) * (L - C)); // 由海倫公式求得三角形面積 db r = A * B * C / (4 * S); // 外接圓半徑 // cout << a << " " << b << " " << c << endl; // cout << A << " " << B << " " << C << endl; // cout << r << endl; // 求圓心座標 Point res; res.x = 0.5 * fun(1, 1, 1, pw(a.x) + pw(a.y), pw(b.x) + pw(b.y), pw(c.x) + pw(c.y), a.y, b.y, c.y) / fun(1, 1, 1, a.x, b.x, c.x, a.y, b.y, c.y); res.y = 0.5 * fun(1, 1, 1, a.x, b.x, c.x, pw(a.x) + pw(a.y), pw(b.x) + pw(b.y), pw(c.x) + pw(c.y)) / fun(1, 1, 1, a.x, b.x, c.x, a.y, b.y, c.y); return Circle(res, r); } Circle two_p_get_cir(Point a, Point b) { // 已知直徑求圓 db r = dis1(a, b) / 2; Point res; res.x = (a.x + b.x) / 2; res.y = (a.y + b.y) / 2; return Circle(res, r); } void solve(){ ll n; cin >> n; vector<Point> p(n); vector<Circle> a; for(int i = 0; i < n; i ++) cin >> p[i]; for(int i = 0; i < n; i ++){ for(int j = i + 1; j < n; j ++){ a.push_back(two_p_get_cir(p[i], p[j])); for(int k = j + 1; k < n; k ++){ a.push_back(three_p_get_cir(p[i], p[j], p[k])); } } } auto cal =[&](Circle c) -> int{ auto [x1, y1] = c.o; db r2 = c.r * c.r; ll ans = 0; for(auto &[x2, y2] : p){ db res = pw(x2 - x1) + pw(y2 - y1); if(sign(res - r2) == 0 || sign(res - r2) == -1) ans ++; } return ans * (ans - 1) / 2; }; vector<db> ans(n * (n - 1) / 2 + 1, INF); for(auto &t : a){ ll num = cal(t); ans[num] = min(ans[num], t.r); } for(int i = n * (n - 1) / 2 - 1; i >= 1; i --) ans[i] = min(ans[i], ans[i + 1]); for(int i = 1; i <= n * (n - 1) / 2; i ++){ cout << fixed << setprecision(8) << ans[i] << '\n'; } }
J. Magic Mahjong
題意:給定一副麻將牌,判斷手上是否有十三么和七對(不打麻將英語還不行的有難了)
思路:模擬即可,包含七個對子或者十三種給定牌
string win[] = {"1z", "2z", "3z", "4z", "5z", "6z", "7z", "1p", "9p", "1s", "9s", "1m", "9m"}; void solve(){ string s; cin >> s; vector<string> pai; vector<ll> ws(10); for(int i = 0; i < s.size(); i += 2){ string s1; s1 += s[i]; s1 += s[i + 1]; pai.push_back(s1); } map<string, ll> mp; for(auto v : pai) mp[v] ++; if(mp.size() == 7){ bool ok = 1; for(auto [x, y] : mp){ if(y != 2) ok = 0; } if(ok == 1){ cout << "7 Pairs\n"; return; } } if(mp.size() == 13){ vector<ll> se(13); for(auto [x, y] : mp){ bool ok = 0; for(int i = 0; i < 13; i ++){ if(x == win[i]){ ok = 1; se[i] = 1; break; } } if(!ok){ cout << "Otherwise\n"; return; } } ll sum = 0; for(auto x : se) sum += x; if(sum == 13) cout << "Thirteen Orphans\n"; else cout << "Otherwise\n"; } else cout << "Otherwise\n"; }
K. Magic Tree
題意:2 * n的路有幾條不同的填充方式
思路:手畫一下發現 2^(n - 1) 即可
ll fastpow(ll a, ll n){ ll res = 1, base = a; while(n){ if(n & 1) res = (res * base) % mod; base = (base * base) % mod; n >>= 1; } return res; } void solve(){ ll n; cin >> n; ll ans = fastpow(2, n - 1); cout << ans << '\n'; }
L. Campus
題意:一個 n 個點 m 條邊的無向圖,每個節點上有 ai 個人。n 個節點中有 k 個 節點為出口,每個出口有一個開放時間 [li , ri ]。求第 1 ∼ T 時刻所有人到 最近的開放出口的路徑長度之和。
思路:預處理出每一種出口對每個人的最短路,然後分情況列舉取min即可
struct Node{ ll y, v; Node(ll _y, ll _v){ y = _y, v = _v; } }; struct gate{ ll id, l, r; bool operator < (gate & a1)const{ return l < a1.l; } }; bool cmp(gate & a1, gate & a2){ return a1.l < a2.l; } vector<Node> edge[N]; ll a[N], res = 0; void solve(){ ll n, m, k, T; cin >> n >> m >> k >> T; string s1; for(int i = 1; i <= k; i ++) s1 += '0'; vector<gate> v(k + 1); for(int i = 1; i <= n; i ++) cin >> a[i]; for(int i = 1; i <= k; i ++){ ll id, l, r; cin >> id >> l >> r; v[i] = {id, l, r}; } for(int i = 1; i <= m; i ++){ ll u, v, w; cin >> u >> v >> w; edge[u].push_back({v, w}); edge[v].push_back({u, w}); } auto dijkstra =[&](ll s, vector<ll> & p) -> void{ vector<ll> dist(n + 1, INF); vector<bool> b(n + 1, false); priority_queue<PLL, vector<PLL>, greater<PLL>> q; dist[s] = 0; for(int i = 1; i <= n; i ++) q.push({dist[i], i}); while(!q.empty()){ ll x = q.top().second; q.pop(); if(b[x]) continue; b[x] = true; for(auto i : edge[x]){ if(dist[x] + i.v < dist[i.y]){ dist[i.y] = dist[x] + i.v; q.push({dist[i.y], i.y}); } } } p = dist; }; vector<vector<ll>> d(k + 1, vector<ll>(n + 1)); for(int i = 1; i <= k; i ++){ dijkstra(v[i].id, d[i]); } // for(int i = 1; i <= k; i ++){ // cout << i << '\n'; // for(int j = 1; j <= n; j ++){ // cout << d[i][j] << ' '; // } // cout << '\n'; // } map<string, vector<ll>> mp; vector<ll> ans(T + 1); for(int i = 1; i <= T; i ++){ string s = s1; for(int j = 1; j <= k; j ++){ if(v[j].l <= i && i <= v[j].r) s[j - 1] = '1'; } mp[s].push_back(i); } for(auto [x, y] : mp){ vector<ll> q(n + 1, INF); bool ok = 0; for(int i = 1; i <= k; i ++){ if(x[i - 1] == '1'){ ok = 1; for(int j = 1; j <= n; j ++){ q[j] = min(q[j], d[i][j]); } } } ll res = 0; if(!ok){ res = -1; } else{ for(int i = 1; i <= n; i ++) res += q[i] * a[i]; } for(auto v : y) ans[v] = res; } for(int i = 1; i <= T; i ++) cout << ans[i] << '\n'; }