The 2024 ICPC Asia East Continent Online Contest (II) - Dashboard - Contest - QOJ.ac Dashboard - The 2024 ICPC Asia EC Regionals Online Contest (II) - Codeforces
官方题解:2024 ICPC 网络赛 第二场简要题解
不会 DP 不会字符串
机器人的活动范围距离起点的距离最大是d d d ,把所有起点扔到队列里,用 BFS 预处理找到到达每个点的步数。
对于某个点,玩家到达步数是x x x ,机器人到达步数是y y y ,则如果x x x 与y y y 的奇偶性相同,且x ⩾ y x \geqslant y x ⩾ y ,这个点就走不能经过。对于同一个点的x , y x,y x , y 可能有多个,只要存在一个x x x ,对于所有的y y y 都满足上述条件即可。因此只需与奇数y y y 或偶数y y y 的最小值比较。BFS 可以保证第一次访问的点就是最小值。
用 BFS 寻路。寻路的时候记录步数x x x ,与同奇偶性的y y y 比较,同时记录到达这个点的前驱,以输出路径。
每个点都可以经过两次,所以 vis
等数组都开成二维的,奇偶相互独立。
注意输出路径时不能写 if (u == 1) break
,可能玩家走 1->2->3->1
改变奇偶性,导致实际路径长度与输出的不符。在 BFS 里走到n n n 就输出返回是比较好的写法。
Bonus:如果数据有d = 0 d=0 d = 0 需特判,则不用考虑所有机器人,因为它们必定原地爆炸。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 void eachT () { int n, m, D; cin >> n >> m >> D; vector<vector<int >> E (n); while (m--) { int u, v; cin >> u >> v; u--, v--; E[u].push_back (v); E[v].push_back (u); } queue<pair<int , int >> Q; vector val (n, array{ inf, inf }) ; int k; cin >> k; while (k--) { int x; cin >> x; x--; val[x][0 ] = 0 ; Q.push ({ 0 , x }); } while (!Q.empty ()) { auto [d, u] = Q.front (); Q.pop (); if (d >= D) continue ; ++d; for (auto & v : E[u]) { if (val[v][d & 1 ] != inf) continue ; val[v][d & 1 ] = d; Q.push ({ d, v }); } } vector from (n, array{ -1 , -1 }) ; Q.push ({ 0 , 0 }); while (!Q.empty ()) { auto [d, u] = Q.front (); Q.pop (); if (u == n - 1 ) { cout << d << '\n' ; vector<int > path; while (d >= 0 ) { path.push_back (u); u = from[u][d & 1 ]; d--; } for (int i = path.size () - 1 ; i >= 0 ; --i) { cout << path[i] + 1 << ' ' ; } cout << '\n' ; return ; } ++d; for (auto & v : E[u]) { if (d >= val[v][d & 1 ]) continue ; if (~from[v][d & 1 ]) continue ; from[v][d & 1 ] = u; Q.push ({ d, v }); } } cout << "-1\n" ; }
每个数的期望次数是固定的,而且贪心地,大数再掷一次,小数等待。
分界线不知道,可以设分界线为x x x ,小于等于x x x 的数等待,记每次掷骰子期望需要 再 等待E E E 秒,有
E = 1 n ( 1 + 2 + ⋯ + x ) + n − x n ( E + 1 ) E=\frac{1}{n}(1+2+\dots+x)+\dfrac{n-x}{n}(E+1) E = n 1 ( 1 + 2 + ⋯ + x ) + n n − x ( E + 1 )
化简得
E = x 2 + n x − 1 2 E=\frac{x}{2}+\frac{n}{x}-\frac{1}{2} E = 2 x + x n − 2 1
是个对勾函数,欲求最小值,取x = 2 n x=\sqrt{2n} x = 2 n 附近的两个正整数最优,比较一下即可。
注意答案需要开 LL。
时间复杂度Θ ( T ) \Theta(T) Θ ( T ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <vector> #include <numeric> #include <cmath> using namespace std;using ll = long long ;void solve () { int n; cin >> n; ll x = sqrt (2 * n); if (x / 2.0 + 1.0 * n / x > (x + 1 ) / 2.0 + 1.0 * n / (x + 1 )) { ++x; } ll f1 = x * x + 2 * n - x; ll f2 = 2 * x; ll d = gcd (f1, f2); f1 /= d, f2 /= d; cout << f1 << ' ' << f2 << '\n' ; } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); int T = 1 ; cin >> T; while (T--) { solve (); } return 0 ; }
平局相当于再来一次,所以每小局概率就是二人获胜概率的比例。
筹码是按照辗转相减的方式减少的,用辗转相除加速辗转相减:
如果甲的筹码比乙少,必须一直赢,直到翻盘; 如果甲的筹码比乙多,每局都可以输或者赢,直至筹码比乙少; 最终两人筹码相同,赢的概率就是小局赢的概率。 具体式子不写了,见代码。
时间复杂度Θ ( T log 2 ) \Theta(T\log^{2}) Θ ( T log 2 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <iostream> using namespace std;using ll = long long ;constexpr int mod = 998244353 ;ll qpow (ll a, ll n) { ll res = 1 ; while (n) { if (n & 1 ) res = res * a % mod; a = a * a % mod; n >>= 1 ; } return res; } ll inv (ll n) { return qpow (n, mod - 2 ); } void solve () { ll x, y, p0, p1, b; cin >> x >> y >> p0 >> p1 >> b; ll ssss = inv (p0 + p1); p0 = p0 * ssss % mod; p1 = p1 * ssss % mod; ll invq = inv (p1 - 1 ); ll res = 0 , pow = 1 ; while (x && y) { if (x == y) { res += pow * p0 % mod; res %= mod; break ; } else if (x > y) { int q = x / y, r = x % y; res += pow * p0 % mod * (qpow (p1, q) - 1 ) % mod * invq % mod; res %= mod; res += mod; res %= mod; pow *= qpow (p1, q); pow %= mod; x = r; } else { int q = y / x, r = y % x; if (r == 0 ) { q--; r = x; } pow *= qpow (p0, q); pow %= mod; y = r; } } cout << res << '\n' ; } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); int T; cin >> T; while (T--) { solve (); } return 0 ; }
首先c i c_{i} c i 和w n + 1 − i w_{n+1-i} w n + 1 − i 的地位等价,不能单独地按某一维排序。需要最大化c 1 , w n c_{1},w_{n} c 1 , w n ,最小化w 1 , c n w_{1},c_{n} w 1 , c n ,而且式子是关于∑ c w \sum cw ∑ c w 的形式,猜测按c w \cfrac{c}{w} w c 降序最优。
用调整法证明:欲求式
S = ∑ i = 1 n ( c i ∑ j = i + 1 n w j ) S=\sum_{i=1}^{n}\left(c_{i}\sum_{j=i+1}^{n}w_{j} \right) S = i = 1 ∑ n ( c i j = i + 1 ∑ n w j )
若记交换k , k + 1 k,k+1 k , k + 1 后得到的结果为S ′ S' S ′ ,则
S ′ − S = c k + 1 w k − c k w k + 1 S'-S=c_{k+1}w_{k}-c_{k}w_{k+1} S ′ − S = c k + 1 w k − c k w k + 1
如果有c k w k < c k + 1 w k + 1 \cfrac{c_{k}}{w_{k}}<\cfrac{c_{k+1}}{w_{k+1}} w k c k < w k + 1 c k + 1 ,则S ′ − S > 0 S'-S>0 S ′ − S > 0 ,即交换后结果不减。因此如果c k w k < c k + 1 w k + 1 \cfrac{c_{k}}{w_{k}}<\cfrac{c_{k+1}}{w_{k+1}} w k c k < w k + 1 c k + 1 ,我们总是可以交换k , k + 1 k,k+1 k , k + 1 使结果更优。因此最终按c w \cfrac{c}{w} w c 降序排序。
读入需要 LL。
时间复杂度Θ ( n log n ) \Theta(n\log n) Θ ( n log n ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <vector> #include <algorithm> using namespace std;using ll = long long ;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); int n; cin >> n; vector<pair<ll, ll>> a (n); ll res = 0 ; for (int i = 0 ; i < n; ++i) { ll w, v, c; cin >> w >> v >> c; res += v; a[i] = { w, c }; } sort (a.begin (), a.end (), [&](const pair<ll, ll>& a, const pair<ll, ll>& b) { return 1ll * a.first * b.second < 1ll * b.first * a.second; }); ll sumc = 0 ; for (int i = 0 ; i < n; ++i) { res -= a[i].first * sumc; sumc += a[i].second; } cout << res << '\n' ; return 0 ; }
低位有1 1 1 的情况下高位0 0 0 可以消掉,如 0 0 0 1 -> 1 -1 -1 -1
。而末尾的0 0 0 无法通过此操作消除,因此末尾有两个及以上的0 0 0 就无解。
时间复杂度Θ ( T log n ) \Theta(T\log n) Θ ( T log n ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <vector> using namespace std;using ll = long long ;void solve () { int n; cin >> n; if (n == 0 || (n & -n) > 2 ) { cout << "NO\n" ; return ; } int D = 32 ; vector<int > a (D) ; for (int bit = 0 ; bit < D; ++bit) { a[bit] = n >> bit & 1 ; if (bit && !a[bit] && a[bit - 1 ]) { a[bit] = 1 ; a[bit - 1 ] = -1 ; } } cout << "YES\n" ; for (int bit = 0 ; bit < D; ++bit) { cout << a[bit] << " \n" [(bit + 1 ) % 8 == 0 ]; } } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); int T = 1 ; cin >> T; while (T--) { solve (); } return 0 ; }
选择容量最小的赛站,最坏高手都在自己这个赛站。按排名从高到低依次统计答案,用桶记次数。
时间复杂度Θ ( n log n ) \Theta(n\log n) Θ ( n log n ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> #include <algorithm> #include <vector> #include <unordered_map> using namespace std;using ll = long long ;const int inf = 0x3f3f3f3f ;struct node { int x; string s; int id; int ans; }; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); int n, k; cin >> n >> k; int minn = inf; for (int i = 0 ; i < k; i++) { int x; cin >> x; minn = min (minn, x); } vector<node> a (n) ; for (int i = 0 ; i < n; i++) { int x; string s; cin >> x >> s; a[i] = { x, s, i, 0 }; } sort (a.begin (), a.end (), [&](const auto & A, const auto & B) { return A.x > B.x; }); unordered_map<string, int > cnt; int sum = 0 ; for (int i = 0 ; i < n; i++) { a[i].ans = sum - (cnt[a[i].s] == minn); if (cnt[a[i].s] < minn) { cnt[a[i].s]++; sum++; } } sort (a.begin (), a.end (), [&](const auto & A, const auto & B) { return A.id < B.id; }); for (int i = 0 ; i < n; i++) { cout << a[i].ans + 1 << endl; } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> using namespace std;using ll = long long ;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); int n; cin >> n; ll sum = 1500 ; for (int i = 0 ; i < n; ++i) { int x; cin >> x; sum += x; if (sum >= 4000 ) { cout << i + 1 << '\n' ; return 0 ; } } cout << -1 << '\n' ; return 0 ; }