2023多校训练+补题记录
牛客第一场
好久没搞算法了,手好生(
D
签到,队友写的(
J
不难发现一次输输输赢的过程可以赢一块钱
问题就转化成了在**m**轮里,是否能每次都在不输完本金的情况下赢一把
显然这个概率是$1-(\frac{1}{2})^t$,$t$表示当前$t$轮输掉本金
$t$显然是从$log n$取到$log (m + n)$
枚举一下$t$,直接暴力乘即可
一开始把题读反了,感觉那个式子还比较有趣(
#include <bits/stdc++.h> #define ll long long using namespace std; ll N,M; const ll MOD = 998244353; ll inv2; ll Pow(ll x,ll y){ ll ans = 1; for (;y; y >>=1){ if(y&1) ans = ans * x % MOD; x = x * x % MOD; } return ans%MOD; } ll pr[55]; int main(){ inv2 = Pow(2,MOD-2); cin >> N >> M; ll x = 1; for (int i = 1 ; i <= 32 ; i ++){ pr[i] = pr[i-1] + x; x *= 2; } ll P = 1,L=0,R=0; for (int i = 1 ; i <= 31 ; i ++){ ll Sum = 0; Sum = (1-Pow(inv2,i)+MOD)%MOD; ll L = pr[i] - N,R = min(pr[i+1]-N,M); if (N > pr[i] && N>pr[i+1]) continue; if (N > pr[i]) L = 0; if (pr[i] > N+M) break; P = P*Pow(Sum,R-L)%MOD; } cout << P << endl; return 0; }
K
考虑先按bfs序把原图变成一颗树
然后会发现如果有一条边,除了树边以外还有别的边与之相连的话,那么那些边随便怎么加点都不影响这个点
所以全部加满
那么考虑什么时候在树边上加点
只有叶子节点且它没有非树边的时候。
因为如果不是这样的话,把这个点加上必然会导致更深处的点被挤,最好的情况只有一个换一个,所以不会更优。
#include <bits/stdc++.h> #define ll long long #define int long long using namespace std; const int mx = 2e5+10; queue<int> Bfs; int N,M,K; vector<int> G[mx]; int ans,dis[mx],pa[mx],Leaf[mx]; signed main(){ cin >> N >> M >> K; for (int i = 1 ; i <= M ; i ++){ int u,v; cin >> u >> v; G[u].push_back(v); G[v].push_back(u); } for (int i = 1 ; i <= N ; i ++) dis[i] = -1; Bfs.push(1); dis[1] = 0; while (!Bfs.empty()){ int u = Bfs.front(); Bfs.pop(); for (auto v : G[u]){ if (dis[v] != -1) continue; Bfs.push(v); dis[v] = dis[u] + 1; pa[v] = u; Leaf[u] = true; } } for (int i = 2 ; i <= N ; i ++){ int u = i; int cnt = 0; if (dis[u] > K || dis[u] == -1) continue; for (auto v : G[i]){ if (pa[v] == u || pa[u] == v) continue; cnt++; } if (!Leaf[u]){ if (cnt == 0) cnt = 1; } ans += cnt * (K-dis[u]) + 1; } cout << ans + 1; return 0; }
H
没有脑子.jpg
考虑交换产生的$\delta$
$|a_i - b_j| + |b_i - a_j| - |a_i - b_i|-|a_j - b_j|$
需要最小化这个东西。设$w_i$ = $a_i - b_i$
分四种情况讨论,下面具体描述其中一种
不妨假设$a_i > b_j$且$b_i > a_j$
于是展开式子就变成最小化$a_i - b_j +b_i - a_j - w_i - w_j$
$a_i + b_i - w_i +( - a_j - b_j - w_j)$
枚举$i$,就是最小化后面那项
条件是$a_i > b_j$ 且 $b_i > a_j$
把$(b_i,a_i)$看成二维平面上的点,这个问题也就变成了一个标准的二维数点问题。抓个数据结构维护即可。
剩下三种情况类似地讨论即可。
然后就被卡了,现在还没过去,噔噔咚(
#include <bits/stdc++.h> using namespace std; const int mx = 1e6; #define ll long long ll inf = 0x3f3f3f3f3f3f; vector<int> num; vector<int> Add[mx+5],Q[mx+5]; int a[mx+10],b[mx+10]; int xx[mx+10],yy[mx+10]; int x[mx+10]; ll Tree1[2*mx+10]; void Clear(){ memset(Tree1,inf,sizeof(Tree1)); } int Lowbit(int x){ return (x&(-x)); } void Add1(int pos, ll nd){ for (int i = pos ; i<= mx ; i +=Lowbit(i)){ Tree1[i] = min(Tree1[i],nd); } } ll Get1(int x){ ll ans = inf; for (int i = x ; i ; i-= Lowbit(i)) ans = min(ans,Tree1[i]); return ans; } signed main(){ ll ans = 0; int N; cin >> N; for (int i = 1 ; i <= N ; i ++){ cin >> a[i]; num.push_back(a[i]); } for (int i = 1 ; i <= N ; i ++){ cin >> b[i]; num.push_back(b[i]); x[i] = abs(a[i] - b[i]); ans += x[i]; } num.push_back(-(1e9 - 100)); sort(num.begin(),num.end()); num.resize(unique(num.begin(),num.end())-num.begin()); int sz = num.size() - 1; for (int i = 1 ; i <= N ; i ++){ xx[i] = lower_bound(num.begin(),num.end(),a[i]) - num.begin(); yy[i] = lower_bound(num.begin(),num.end(),b[i]) - num.begin(); Add[xx[i]].push_back(i); Q[yy[i]].push_back(i); } Clear(); ll mn = inf; for (int i = 1 ; i <= sz ; i ++){ for (auto v : Add[i]){ Add1(yy[v],-a[v]-b[v]-x[v]); } for (auto t : Q[i]){ mn = min(mn,a[t] + b[t] + Get1(xx[t]) - x[t]); } } Clear(); for (int i = sz ; i >= 1 ; i --){ for (auto v : Add[i]){ Add1(yy[v],a[v]-b[v]-x[v]); } for (auto t : Q[i]){ mn = min(mn,a[t] - b[t] + Get1(xx[t]) - x[t]); } } Clear(); for (int i = 1 ; i <= N ; i ++) xx[i] = sz-xx[i] + 1; for (int i = 1 ; i <= N ; i ++) yy[i] = sz-yy[i] + 1; for (int i = 1 ; i <= sz ; i ++){ for (auto v : Add[i]){ Add1(yy[v],-a[v]+b[v]-x[v]); } for (auto t : Q[i]){ mn = min(mn,-a[t] + b[t] + Get1(xx[t]) - x[t]); } } Clear(); for (int i = sz ; i>= 1 ; i --){ for (auto v : Add[i]){ Add1(yy[v],a[v]+b[v]-x[v]); } for (auto t : Q[i]){ mn = min(mn,-a[t] - b[t] + Get1(xx[t]) - x[t]); } } cout << ans + mn; return 0; }
干啥啥不行