# 第三次社內賽題解 --- ### A. 電研社的工作 ---- 每天選一個人放假組合數=每天可以放假的人數相乘 ---- subtask 1 所有人每天都可以放假,所以答案$=k^n$ $O(n\times k)$ ---- full score 剪一剪就可以算出每天可以放假的人數 $O(n\times k)$ ---- ```cpp= #include <bits/stdc++.h> #define pb push_back #define lc 2*v #define rc 2*v+1 #define f first #define s second #define rep(X, a,b) for(int X=a;X<b;++X) #define ALL(a) (a).begin(), (a).end() #define SZ(a) (int)(a).size() #define NL "\n" using namespace std; typedef pair<long long,long long> pll; typedef pair<int,int> pii; typedef long long ll; template<typename A, typename B> ostream& operator<<(ostream &os, const pair<A, B> &p) { return os << '(' << p.first << "," << p.second << ')'; } template<typename A> ostream& operator<<(ostream &os, const vector<A> &p){ for(const auto &a:p) os << a << " "; os << "\n"; return os; } int cnt[10010]; const ll mod=1e9+7; int main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, k; cin>>n>>k; rep(i,0,k){ int t; cin>>t; set<int> st; rep(j,0,t){ string a; cin>>a; int res=0; for(auto c:a){ res=(res+(c-'0'))%n; } if(st.count(res)==0) cnt[res]++; st.insert(res); } } ll ans=1; rep(i,0,n){ ans=ans*(k-cnt[i])%mod; } cout<<ans<<NL; } ``` --- ### B. 排序子字串查詢 ---- subtask 1 暴力做 $O(q\times |s|\log |s|)$ ---- full score 開一個26大小的陣列存每個字母的數量(類似counting sort)就可以做到跟暴力排序一樣的效果。 修改時把原本的字母數量減1,新加的加1 查詢則從陣列l~r看是否=k,但頭尾可以>=k $O(q)$ ---- code ```cpp= #include <bits/stdc++.h> using namespace std; int cnt[30]; int main(){ ios::sync_with_stdio(0); cin.tie(0); string s; cin>>s; for(auto a:s) cnt[a-'a']++; int q; cin>>q; while(q--){ int t; cin>>t; if(t==1){ int x; char c; cin>>x>>c; cnt[s[x]-'a']--; s[x]=c; cnt[s[x]-'a']++; } else{ int l, r, k; cin>>l>>r>>k; int flag=1; for(int i=l;i<=r;++i){ if(cnt[i]<k){ flag=0; break; } if(i>l && i<r && cnt[i]!=k){ flag=0; break; } } if(!flag) cout<<"No\n"; else cout<<"Yes\n"; } } } ``` --- ### C. 最小陣列差 ---- subtask 1 $O(n^2)$暴力 ---- full score 方法一:對兩陣列排序後雙指針,一個指針在B一一遞進,另一個在A維護最大<=B指針的數字。$O(n)$ 方法二:排序B陣列,迴圈跑A陣列,每次二分搜B最接近目前A的數字。$O(n\log n)$ ---- 二分搜code ```cpp= #include <bits/stdc++.h> #define ll long long using namespace std; int main(){ ios_base::sync_with_stdio(false); cin.tie(0); int n, m; cin>>n>>m; vector<ll> a(n), b(m); for(int i=0;i<n;++i) cin>>a[i]; for(int i=0;i<m;++i) cin>>b[i]; sort(b.begin(), b.end()); ll mn=1e15; for(int i=0;i<n;++i){ auto tmp=lower_bound(b.begin(), b.end(), a[i]); if(tmp==b.begin()) mn=min(mn, *tmp-a[i]); else if(tmp==b.end()) mn=min(mn, a[i]-*(tmp-1)); else mn=min(mn, min(*tmp-a[i], a[i]-*(tmp-1))); } cout << mn << "\n"; } ``` --- ### D. 群島高鐵系統 ---- subtask 1 裸dijkstra $O(n\log v)$ ---- subtask 2 m=n-1為一棵樹,可以用某種樹dp $O(n)$ ---- full score 一樣做dijkstra,只要在priority_queue多存走過的城市數量,每次走一條邊就可以算權重了。 $O(n\log v)$ ---- 正確性? 每過3站加k的條件代表走越少邊越好,所以跟bfs吻合 ---- code ```cpp= #include <bits/stdc++.h> #define ll long long #define pll pair<ll,ll> using namespace std; struct dt{ ll tot, cnt, v; }; struct cmp{ bool operator()(dt &d1, dt &d2){ return d1.tot>d2.tot; } }; int is[200010], done[200010]; vector<int> adj[200010]; ll dis[200010]; int main(){ ios::sync_with_stdio(0); cin.tie(0); int n, m, c, k; cin>>n>>m>>c>>k; for(int i=1;i<=n;++i) cin>>is[i]; for(int i=0;i<m;++i){ int a, b; cin>>a>>b; adj[a].push_back(b); adj[b].push_back(a); } priority_queue<dt, vector<dt>, cmp> pq; fill(dis, dis+n+1, 1e14); pq.push({dis[1]=0, 1, 1}); while(!pq.empty()){ auto g=pq.top(); pq.pop(); if(done[g.v]) continue; done[g.v]=1; for(auto a:adj[g.v]){ ll cur=g.tot; if((g.cnt+1)%3==0) cur+=c; if(is[a]!=is[g.v]) cur+=k; if(cur<dis[a]){ dis[a]=cur; pq.push({cur, g.cnt+1, a}); } } } cout<<dis[n]<<"\n"; } ``` --- ### E. 背包問題改 ---- subtask 1 $O(2^n)$枚舉所有取法 ---- subtask 2 $dp[i][j][w]=$考慮到前$i$個物品,總價值=j,且取了w個物品的方法數 轉移:$dp[i][j][w]=dp[i-1][j][w]+dp[i-1][j-v_i][w-1]$ 答案:$dp[n][k][n-2]$ 另解:https://codeforces.com/group/50laGF4JJZ/contest/438519/submission/202365802 $O(n^2\times k)$ ---- full score 答案 = 總價值為k的方法數 - (取n個價值為k的方法數 + 取n-1個價值為k的方法數) *總價值為k的方法數*:用一般的背包 *取n個價值為k的方法數*:看總和是否=k *取n-1個價值為k的方法數*:枚舉少取哪一個數$O(n)$ $O(n\times k)$ ---- code ```cpp= #include <bits/stdc++.h> #define ll long long using namespace std; ll mod=1e9+7; ll val[210], dp[100010]; int main(){ int n, k; cin>>n>>k; int tot=0; for(int i=1;i<=n;++i) cin>>val[i], tot+=val[i]; dp[0]=1; for(int i=1;i<=n;++i){ for(int j=k;j>=val[i];--j){ dp[j]+=dp[j-val[i]]; dp[j]%=mod; } } ll minus=0; if(tot==k) minus++; for(int i=1;i<=n;++i) if(tot-val[i]==k) minus++; cout<<dp[k]-minus<<"\n"; } ``` --- ### F. 最遠距離 ---- subtask 1 $O(n\times q)$暴力 ---- spoiler subtask 2 set維護目前在的人,加入時二分搜他左右的人,更新答案 $O(q\log n)$ ---- full score 線段樹應用 每個節點存此區間最右,最左的人,以及這區間最大的距離 ```cpp struct nd{ int ll=-1, rr=-1, mx=-1; void pull(nd L, nd R){ //pull為合併兩子樹 if(R.rr==-1) rr=L.rr; else rr=R.rr; if(L.ll==-1) ll=R.ll; else ll=L.ll; mx=max(L.mx, R.mx); if(R.ll!=-1 && L.rr!=-1) mx=max(mx, R.ll-L.rr); } } ``` ---- code ```cpp= #include <bits/stdc++.h> using namespace std; struct nd{ int ll=-1, rr=-1, mx=-1; void pull(nd L, nd R){ if(R.rr==-1) rr=L.rr; else rr=R.rr; if(L.ll==-1) ll=R.ll; else ll=L.ll; mx=max(L.mx, R.mx); if(R.ll!=-1 && L.rr!=-1) mx=max(mx, R.ll-L.rr); } }; nd tree[800010]; void upd(int v, int l, int r, int pos, int x){ if(l==r){ if(x==1) tree[v]={l, l, -1}; else tree[v]={-1, -1, -1}; return; } int mid=(l+r)>>1; if(pos<=mid) upd(2*v, l, mid, pos, x); else upd(2*v+1, mid+1, r, pos, x); tree[v].pull(tree[2*v], tree[2*v+1]); // cout<<l<<" "<<r<<" "<<tree[v].ll<<" "<<tree[v].rr<<" "<<tree[v].mx<<"\n"; } nd get(int v, int l, int r, int ql, int qr){ if(l==ql && r==qr) return tree[v]; int mid=(l+r>>1); if(qr<=mid) return get(2*v, l, mid, ql, qr); else if(ql>mid) return get(2*v+1, mid+1, r, ql, qr); nd resl=get(2*v, l, mid, ql, mid), resr=get(2*v+1, mid+1, r, mid+1, qr), ret; ret.pull(resl, resr); return ret; } int main(){ ios::sync_with_stdio(0); cin.tie(0); int n, q; cin>>n>>q; while(q--){ int t; cin>>t; if(t==3){ int l, r; cin>>l>>r; cout<<get(1, 1, n, l, r).mx<<"\n"; } else{ int x; cin>>x; upd(1, 1, n, x, t); } } } ```
{"metaMigratedAt":"2023-06-18T01:54:08.947Z","metaMigratedFrom":"YAML","title":"第三次社內賽題解","breaks":true,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"9afea478-d7aa-4c3e-a3c7-5db22422f956\",\"add\":7643,\"del\":395}]"}
    234 views