天梯賽選拔第二場:
L2-1:佇列模擬
void solve() { int n,k; cin >> n >> k; stack<int> q,box; for(int i=1;i<=n;i++) { int x; cin >> x; q.push(x); } stack<int> res; vector<int> ans; while(q.size() || box.size()) { if(q.size()) { int x = q.top(); q.pop(); if(res.empty()) res.push(x); else { int y = res.top(); if(x>y && x-y<=k) res.push(x); else { if(box.size()) { int t = box.top(); if(t>y && t-y<=k) { res.push(t); box.pop(); } } box.push(x); } } } if(q.empty()) { q = box; while(box.size()) box.pop(); if(res.size()==1) { int t = res.top(); res.pop(); ans.push_back(t); } else { while(res.size()) cout << res.top() << ' ' , res.pop(); cout << endl; } } } for(auto & t : ans) cout << t << ' '; }
L2-3:n有1e5大小。所以對於每一個數字,預處理每一個點長度和各個位數之和。然後對於每一個 x ,令總和為 w ,列舉前i位之和,去找滿足條件的abs(w-x)的個數。
PII get(int x) { PII res; while(x) { res.first++; res.second+=x%10; x/=10; } return res; } vector<int> get_(int x) { vector<int> v; while(x) v.push_back(x%10) ,x/=10; reverse(v.begin() , v.end()); return v; } void solve() { int n; cin >> n; map<int , int> mp; map<int , int> len,sum,mp1,mp2,mp3,mp4; for(int i=1;i<=n;i++) { int w; cin >> w; mp[w]++; PII x = get(w); len[w]=x.first , sum[w]=x.second; if(x.first&1 && x.second&1) mp1[x.second]++; else if(x.first&1 && x.second%2==0) mp2[x.second]++; else if(x.first%2==0 && x.second&1) mp3[x.second]++; else mp4[x.second]++; } int ans=0; for(auto & [s,b] : mp) { int x = 0 , y = sum[s] , res=0; vector<int> v; v = get_(s); for(int j=0;j<v.size();j++) { x += v[j] , y -= v[j]; // cout << x << ' ' << y << endl; int z = abs(x-y); // cout << z << endl; if(len[s]&1) { if(sum[s]&1) res+=mp1[z]; else res+=mp2[z]; } else { if(sum[s]&1) res+=mp3[z]; else res+=mp4[z]; } } // cout << "res: " << res << endl; ans+=res*b; // cout << ans << endl << endl; } cout << ans << endl; }
L2-4:優先佇列小頂堆處理每一個節點,直到不能取為止。
void solve() { int n,m,x,y,t; cin >> n >> m >> x >> y >> t; vector<vector<int>> v(n+10,vector<int>(m+10)); vector<vector<int>> f(n+10,vector<int>(m+10)); vector<vector<bool>> vis(n+10,vector<bool>(m+10)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { cin >> v[i][j] , f[i][j]=v[i][j]; } } while(t--) { int a,b; cin >> a >> b; f[a][b]=-1; } f[x][y]=-1; priority_queue<PII , vector<PII> , greater<PII>> q; q.push({f[x][y],{x,y}}); int power=0; while(q.size()) { int w = q.top().first , xt = q.top().second.first , yt = q.top().second.second; q.pop(); if(vis[xt][yt]) continue; vis[xt][yt]=1; if(power >= w) { power += v[xt][yt]; for(int i=1;i<=4;i++) { int xx = xt + dx[i]; int yy = yt + dy[i]; if(xx>=1 && xx<=n && yy>=1 && yy<=m && !vis[xx][yy]) { q.push({f[xx][yy],{xx,yy}}); } } } } cout << power << endl; }
L3-1:dp:令 f[n][k] 表示前n個數選k組得到的最大和。對於每組是是那個節點到哪個節點,只要
f[n][k]-f[n-m][k-1] == vis[n]-vis[n-m] 即表示這一段被選擇。
void solve() { int n,m,k; cin >> n >> m >> k; vector<int> v(n+10),vis(n+10); vector<vector<int>> f(n+10,vector<int>(k+10)); for(int i=1;i<=n;i++) cin >> v[i] , vis[i]=vis[i-1]+v[i]; for(int i=m;i<=n;i++) { for(int j=1;j<=k;j++) { f[i][j]=max(f[i-1][j],f[i-m][j-1]+vis[i]-vis[i-m]); } } // for(int i=1;i<=n;i++) { // for(int j=1;j<=k;j++) cout << f[i][j] << " \n"[j==k]; // } vector<pair<int , int>> ans; int i = n , j = k; while(k) { if(f[i][k]-f[i-m][k-1]==vis[i]-vis[i-m]) { ans.push_back({i-m+1,i}); i=i-m; k--; } else i--; } cout << f[n][j] << endl; reverse(ans.begin() , ans.end()); for(auto & [a,b] : ans) cout << a << ' ' << b << endl; }
L3-2:對於一棵樹,每次有兩種操作,要麼使某個節點值加 x ;要麼求某一個節點形成的子樹的價值是多少。
dfs序雜湊一下每個節點,然後樹狀陣列維護字首和即可。
// /l、 //(゚、 。 7 // l、 ~ヽ // じしf_, )ノ // 不要放棄!貓貓會為你加油! #include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std; const int N = 5e5+10 , M = 1e6+10 ; int n,m,cnt; int tr[N],dis[N]; vector<int> v[N]; map<int , int> mp; int lowbit(int x) { return x & -x; } void add(int x,int d) { for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=d; } int query(int x) { int res=0; for(int i=x;i;i-=lowbit(i)) res+=tr[i]; return res; } int dfs(int x) { mp[x]=++cnt; dis[mp[x]]=1; for(auto & t : v[x]) { dis[mp[x]]+=dfs(t); } return dis[mp[x]]; } void solve() { cin >> n >> m; vector<int> w(n+10); for(int i=1;i<=n;i++) cin >> w[i]; for(int i=2;i<=n;i++) { int x; cin >> x; v[x].push_back(i); } dfs(1); // for(int i=1;i<=n;i++) cout << i << ' ' << mp[i] << ' ' << dis[mp[i]] << endl; for(int i=1;i<=n;i++) add(mp[i],w[i]); // for(int i=1;i<=n;i++) cout << query(mp[i]) - query(mp[i]-1) << endl; while(m--) { int op; cin >> op; if(op&1) { int x,y; cin >> x >> y; add(mp[x],y); } else { int x; cin >> x; cout << query(mp[x]+dis[mp[x]]-1) - query(mp[x]-1) << endl; } } } signed main() { ios::sync_with_stdio(0); cin.tie(0) , cout.tie(0); int T = 1; // cin >> T ; while(T--) solve(); return 0; }
L3-3:求 i^j 的總和。
1 <= i <= n , 1 <= j <= n , 1 <= n <= 1e18。
從二進位制角度來看,其實就是每一位 0 的數量乘以 1 的數量在乘以當前 2 的 i 次方即可。(要開__int28)
// /l、 //(゚、 。 7 // l、 ~ヽ // じしf_, )ノ // 不要放棄!貓貓會為你加油! #include <bits/stdc++.h> #define endl '\n' #define int __int128 using namespace std; const int N = 2e5+10 , M = 1e6+10 , mod = 998244353; void solve() { long long n; cin >> n; int ans=0; for(int i=60;i>=0;i--) { int d = (1ll<<i) , w=d-1; if(n<=w) continue; int s = n-w; int a = s/(d*2ll)*d , b = a; if(s % (d*2ll)) { int y = s % (d*2ll); if(y<d) a+=y; else a+=d , y-=d , b+=y; } b+=w; ans = (ans + a % mod * b % mod * d % mod * 2ll % mod) % mod; } cout << (long long)ans << endl; } signed main() { ios::sync_with_stdio(0); cin.tie(0) , cout.tie(0); long long T = 1; cin >> T ; while(T--) solve(); return 0; }
SMU 2024 spring 天梯賽1:
7-11:龍龍送外賣
有一棵樹,樹根是外賣店,節點上有些地方有人點外賣,問:每當有人點外賣之後,龍龍把外賣送給所有人至少需要走多少步,每兩個節點之間的距離是1。
解:畫一下圖可以發現,龍龍所走的最短距離就是所有被覆蓋的點的距離*2-離外賣店最遠的那個節點。dfs處理一下正序每個節點離外賣店的距離。倒序往回標記有哪些節點被走過。記錄一下最遠節點的距離輸出即可。
void dfs(int x,int w) { dis[x]=w; for(auto & t : ve[x]) { dfs(t,w+1); } } void DFS(int x) { vis[x]=1 , cnt++; for(auto & t : v[x]) { if(!vis[t]) DFS(t); } } void solve() { int root; cin >> n >> m; for(int i=1;i<=n;i++) { int x; cin >> x; if(x==-1) root=i; else { v[i].push_back(x); ve[x].push_back(i); } } vis[root]=1; dfs(root,0); int res=0; while(m--) { int x; cin >> x; if(!vis[x]) DFS(x); res=max(res,dis[x]); cout << cnt*2-res << endl; } }
7-12:智慧護理中心統計
也是和黃金樹影一樣的,dfs序加樹狀陣列處理即可。
int n,m,cnt=0,idx=0; vector<int> v[N]; unordered_map<string , int> mp; int tr[N],dis[N],ru[N],hx[N]; unordered_map<int , string> lao; bool vis[N]; int lowbit(int x) { return x & -x; } void add(int x,int d) { for(int i=x;i<=cnt;i+=lowbit(i)) tr[i]+=d; } int query(int x) { int res=0; for(int i=x;i;i-=lowbit(i)) res+=tr[i]; return res; } int dfs(int x) { vis[x]=1; hx[x]=++idx; dis[hx[x]]=1; for(auto & t : v[x]) { if(!vis[t]) dis[hx[x]]+=dfs(t); } return dis[hx[x]]; } void solve() { cin >> n >> m; vector<pair<int , string>> res; for(int i=1;i<=m;i++) { string A,B; cin >> A >> B; if(A[0]>='A') { if(!mp.count(A)) mp[A]=++cnt; if(!mp.count(B)) mp[B]=++cnt; int a = mp[A] , b = mp[B]; v[b].push_back(a); ru[a]++; } else { int d=0; for(int j=0;j<A.size();j++) d = d*10 + A[j]-'0'; res.push_back({d,B}); if(!mp.count(B)) mp[B]=++cnt; } } for(auto & t : mp) { if(!ru[t.second]) dfs(mp[t.first]); } for(auto & [x,s] : res) { add(hx[mp[s]],1); lao[x]=s; } char op; while(cin >> op && op!='E') { if(op=='Q') { string s; cin >> s; cout << query(hx[mp[s]]+dis[hx[mp[s]]]-1) - query(hx[mp[s]]-1) << endl; } else { int x; string s; cin >> x >> s; if(lao.count(x)) { string w = lao[x]; add(hx[mp[w]],-1); add(hx[mp[s]],1); } else { add(hx[mp[s]],1); } lao[x]=s; } } }