歷屆第4題
2016年3月
血緣關係
提示
樹
題解
這題就是在找樹上的最遠距離
建圖,然後從隨便一個點開始dfs
dfs的時候維護"某一方向最多可以延伸多遠"
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll n;
while (cin >> n){
vector<vector<ll>> connect(n);
for (int i = 1;i<n;i++){
ll a,b;
cin >> a >> b;
connect[a].push_back(b);
connect[b].push_back(a);
}
ll ans = 0;
function<ll(ll,ll)> dfs;
dfs = [&](ll cur, ll p){
ll h = 0;
for (ll t : connect[cur]){
if (t!=p){
ll hh = dfs(t,cur);
ans = max(ans,hh+h);
h = max(h,hh);
}
}
return h+1;
};
dfs(0,-1);
cout << ans << "\n";
}
}
Python範例代碼:
2016年10月
棒球遊戲
注:本題難度不符合第四題應有的難度
提示
題解
C++範例代碼:
Python範例代碼:
2017年3月
基地台
提示
二分搜、貪心法
題解
對直徑二分搜
用貪心法檢查直徑是否足夠
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll n,k;
cin >> n >> k;
vector<ll> p(n);
for (int i = 0;i<n;i++) cin >> p[i];
sort(p.begin(),p.end());
function<bool(ll)> ok = [&](ll d){
ll c = 1;
ll sp = p[0];
for (ll i : p){
if (i>d+sp){
sp = i;
c++;
}
}
return c<=k;//ans<=d
};
ll l = 1,r = 1e9;
while (l<r){
ll mid = (l+r)/2;
if (ok(mid)){
r = mid;
}else{
l = mid+1;
}
}
cout << l << "\n";
}
Python範例代碼:
2017年10月
物品堆疊
提示
排序
題解
考慮只有兩個物品的情況
會發現 較小的在上方較優
拓展到一般情況
可以以此規則做排序後直接計算能量消耗值
即為答案
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
bool cmp(pair<ll,ll> &a, pair<ll,ll> b){
return a.first*b.second<a.second*b.first;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll n;
cin >> n;
vector<pair<ll,ll>> a(n);
for (int i = 0;i<n;i++) cin >> a[i].first;
for (int i = 0;i<n;i++) cin >> a[i].second;
sort(a.begin(),a.end(), cmp);
ll ans = 0;
ll w = 0;
for (int i = 0;i<n;i++){
ans += w*a[i].second;
w += a[i].first;
}
cout << ans << "\n";
}
Python範例代碼:
2019年6月
美麗的彩帶
提示
unordered_map
題解
用unordered_map存各個數字的數量
再由左往右掃描每一個寬度為 的區間
注意數字要用string
存
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll m,n;
cin >> m >> n;
vector<string> a(n);
for (ll i = 0;i<n;i++){
cin >> a[i];
}
ll tps = 0;
unordered_map<string,ll> mp;
function<void(string&)> add = [&](string &s){
if (!mp.count(s)){
mp[s] = 0;
}
if (mp[s]==0) tps++;
mp[s]++;
};
function<void(string&)> del = [&](string &s){
mp[s]--;
if (mp[s]==0) tps--;
};
ll ans = 0;
for (ll i = 0;i<m;i++){
add(a[i]);
}
ans += tps>=m;
for (ll i = m;i<n;i++){
add(a[i]);
del(a[i-m]);
ans += tps>=m;
}
cout << ans << "\n";
}
Python範例代碼:
2020年1月
貨物分配
提示
樹
題解
直接模擬物品放入的過程
過程中順便維護"切換器下貨箱總重量"
C++範例代碼:
#include <bits/stdc++.h>
using namespace std;
vector<int> add;
vector<int> weight;
vector<int> left_node;
vector<int> right_node;
int n, m;
int init(int cur){
if (cur<n) weight[cur] = init(left_node[cur])+init(right_node[cur]);
return weight[cur];
}
int main(){
ios::sync_with_stdio(false);cin.tie();
cin >> n >> m;
add.resize(m,0);
weight.resize(2*n,0);
left_node.resize(2*n,0);
right_node.resize(2*n,0);
for (int i = 0;i<n;i++){
cin >> weight[n+i];
}
for (int i = 0;i<m;i++){
cin >> add[i];
}
for (int i = 1;i<n;i++){
int p,s,t;
cin >> p >> s >> t;
left_node[p] = s;
right_node[p] = t;
}
init(1);
for (int i = 0;i<m;i++){
int w = add[i];
int cur = 1;
while (cur<n){
weight[cur]+=w;
if (weight[left_node[cur]]<=weight[right_node[cur]]){
cur = left_node[cur];
}else{
cur = right_node[cur];
}
}
weight[cur]+=w;
cout << cur << " \n"[i==m-1];
}
}
Python範例代碼:
2020年7月
病毒演化
提示
樹、dp
題解
觀察可得每個位置都是獨立的
分別處裡每一個位置
從根開始dfs,過程中dp維護"選某一個字元後,該點以下的距離總合最小值"
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll n,m;
cin >> n >> m;
vector<string> virus(n);
vector<vector<ll>> childs(n);
ll root = -1;
for(ll x = 0;x<n;x++){
ll i,j;
cin >> i >> j >> virus[i-1];
if(i!=j) childs[j-1].push_back(i-1);
else root = i-1;
}
vector<vector<ll>> dp(n);
const string chs = "AUCG";
const ll big = 1e10;
ll ans = 0;
ll p;
function<void(ll)> dfs;
dfs = [&](ll i){
for (ll ch : childs[i]) dfs(ch);
char c = virus[i][p];
for (int a = 0;a<4;a++){
if(c == '@' || c == chs[a]){
ll x = 0;
for (ll ch : childs[i]){
ll m = big;
for (int b = 0;b<4;b++){
m = min(m,dp[ch][b]+(a!=b));
}
x+=m;
}
dp[i][a] = x;
}
}
};
for (p = 0;p<m;p++){
for (ll i = 0;i<n;i++) dp[i].assign(4,big);
dfs(root);
ll curans = big;
for (int a = 0;a<4;a++) curans = min(curans,dp[root][a]);
ans += curans;
}
cout << ans << "\n";
}
Python範例代碼:
2020年10月
低地距離
提示
線段樹
題解
從小的數字開始處理
低窪值即為兩個數字之間有幾個處裡過的數字
利用線段樹維護"有幾個處裡過的數字"
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
struct segtree{
ll n;
vector<ll> dat;
segtree(ll N):n(N){
dat.resize(4*n,0LL);
}
void add(ll i,ll l,ll r,ll p){
dat[i]++;
if (l<r){
ll mid = (l+r)/2;
if (mid>=p){
add(i<<1,l,mid,p);
}else{
add(i<<1|1,mid+1,r,p);
}
}
}
void add(ll p){
add(1,0,n-1,p);
}
ll query(ll i,ll l,ll r,ll ql,ll qr){
ll x = 0;
if (ql<=l&&qr>=r){
x += dat[i];
}else{
ll mid = (l+r)/2;
if (mid>=ql){
x+=query(i<<1,l,mid,ql,qr);
}
if (mid<qr){
x+=query(i<<1|1,mid+1,r,ql,qr);
}
}
return x;
}
ll query(ll ql,ll qr){
return query(1,0,n-1,ql,qr);
}
};
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll n;
cin >> n;
vector<vector<ll>> dat(n);
for (ll i = 0;i<2*n;i++){
ll x;
cin >> x;
dat[x-1].push_back(i);
}
segtree t(2*n);
ll ans = 0;
for (ll i =0;i<n;i++){
ll a = min(dat[i][0],dat[i][1]);
ll b = max(dat[i][0],dat[i][1]);
ans += t.query(a,b);
t.add(a);
t.add(b);
}
cout << ans << "\n";
}
Python範例代碼:
2021年1月
飛黃騰達
提示
dp
題解
觀察可得
飛黃吃到的果實在依其中一個座標軸排序後
另一個座標軸的值是(非嚴格)遞增的
所以可以依其中一個座標軸排序後求最長遞增子序列LIS
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
bool cmp(const pair<ll,ll> &a, const pair<ll,ll> &b){
return a.first==b.first?a.second>b.second:a.first<b.first;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll n;
cin >> n;
vector<pair<ll,ll>> a(n);
for(ll i = 0;i<n;i++) cin >> a[i].first >> a[i].second;
sort(a.begin(),a.end());
vector<ll> dp;
for (pair<ll,ll> &o : a){
ll i = o.second;
vector<ll>::iterator it = upper_bound(dp.begin(),dp.end(),i);
if (it == dp.end()) dp.push_back(i);
else{
*it = i;
}
}
cout << dp.size() << "\n";
}
Python範例代碼:
2021年9月
美食博覽會
注:這題ZeroJudge測資太爛, 可以過,但在此還是介紹好一點的作法
提示
dp
題解
先求出每個點做多可以往左吃幾間店
再用dp求答案
C++範例代碼:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll n,k;
cin >> n >> k;
vector<ll> a(n);
for(ll i = 0; i<n; i++){
cin >> a[i];
a[i]--;
}
vector<ll> left(n);
left[0] = 0;
vector<bool> used(n,false);
used[a[0]] = true;
ll l = 0;
for (ll i = 1; i<n;i++){
while (used[a[i]]){
used[a[l]] = false;
l++;
}
left[i] = l;
used[a[i]] = true;
}
vector<vector<ll>> dp(n+1);
for (auto &o : dp) o.resize(k+1,0);
for (ll i = n;i>0;i--){
for (ll j = k;j>0;j--){
dp[left[i-1]][j] = max(dp[left[i-1]][j],dp[i][j-1]+(i-left[i-1]));
}
for (ll j = 0;j<=k;j++){
dp[i-1][j] = max(dp[i-1][j],dp[i][j]);
}
}
ll ans = 0;
for (ll j = 0;j<=k;j++){
ans = max(ans,dp[0][j]);
}
cout << ans << "\n";
}
Python範例代碼:
2021年11月
真假子圖
提示
dsu
題解
用並查集處理
為每個人創造鏡像
看到兩個人合作,將兩人與對方的鏡像放到同一組
如果有人和自己的鏡像同一組就代表出現了矛盾
C++範例代碼:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct dsu{
ll n;
vector<ll> p;
ll gpc;
dsu(ll N):n(N){
p.resize(n);
reset();
}
void reset(){
gpc = n;
for(ll i = 0; i < n; i++) p[i]=i;
}
ll gp(ll i){
if (i == p[i]) return i;
return p[i] = gp(p[i]);
}
bool merge(ll a,ll b){
if (con(a,b)) return false;
p[gp(a)] = gp(b);
gpc--;
return true;
}
bool con(ll a,ll b){
return gp(a)==gp(b);
}
};
int main(){
ios::sync_with_stdio(false);cin.tie();
ll n,m;
cin >> n >> m;
vector<pair<ll,ll>> a(m);
for(ll i = 0; i < m; i++) cin >> a[i].first >> a[i].second;
ll p,k;
cin >> p >> k;
dsu d(2*n);
for (auto &o : a) {
d.merge(o.first, o.second+n);
d.merge(o.first+n, o.second);
}
for (ll x = 1; x<=p;x++){
vector<pair<ll,ll>> c(k);
for(ll i = 0; i < k; i++) cin >> c[i].first >> c[i].second;
for (auto &z : c) {
d.merge(z.first, z.second+n);
d.merge(z.first+n, z.second);
if (d.con(z.first,z.first+n)){
cout << x << "\n";
d.reset();
for (auto &o : a) {
d.merge(o.first, o.second+n);
d.merge(o.first+n, o.second);
}
break;
}
}
}
}
Python範例代碼:
2022年1月
牆上海報
提示
貪心法、二分搜
題解
用貪心法可 求出某一高度能否貼海報
能否貼海報 此高度 最大高度 答案
且已知答案為 中的一個整數
對 二分搜
C++範例代碼:
#include <bits/stdc++.h>
using namespace std;
int n,k;
vector<int> h,w;
bool check(int ans){
int t = 0,len=0;
for (int i = 0;i<n;i++){
if (h[i]>=ans) len++;
else len = 0;
if(len>=w[t]){
len-=w[t];
t++;
if(t>=k) return true;
}
}
return false;
}
int search_ans(int l, int r){
if (l == r) return l;
int mid = ((r+l)/2)+1;
if (check(mid)){
return search_ans(mid,r);
}
return search_ans(l,mid-1);
}
int main(){
ios::sync_with_stdio(false);cin.tie();
cin >> n >> k;
h.resize(n);
w.resize(k);
int mh = 0;
for (int i = 0;i<n;i++){
cin>>h[i];
if(h[i]>mh) mh=h[i];
}
for (int i = 0;i<k;i++){
cin>>w[i];
}
cout << search_ans(1,mh) << "\n";
}
Python範例代碼:
n=k=0
h=[]
w=[]
def check(ans):
t = l = 0
for i in range(n):
if h[i]>=ans:
l+=1
else:
l=0
if l>=w[t]:
l-=w[t]
t+=1
if t>=k:
return True
return False
def search(l,r):
if l==r:
return l
mid = (l+r)//2+1
if check(mid):
return search(mid,r)
return search(l,mid-1)
n,k = map(int,input().split())
h = [int(s) for s in input().split()]
w = [int(s) for s in input().split()]
print(search(1,max(h)))
2022年6月
內積
提示
dp
題解
用各種排列方式對齊後,直接兩兩相乘,做最大連續區間和
即掃描一次序列 求 目前前綴和 - 最小前綴和 之最大值
C++範例代碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll solve(vector<ll> &a, vector<ll> &b){
ll la = a.size();
ll lb = b.size();
ll ans = -1e18;
for (ll d = -la+1;d<=lb-1;d++){
ll s = 0;
ll p = 0;
for (ll i = max(0LL,-d); i<min(la,lb-d);i++){
s += a[i]*b[i+d];
ans = max(ans,s-p);
p = min(p,s);
}
}
return ans;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
ll m,n;
cin >> m >> n;
vector<ll> x(m),y(n);
for (ll &i : x) cin >> i;
for (ll &i : y) cin >> i;
ll ans = solve(x,y);
reverse(y.begin(),y.end());
ans = max(ans,solve(x,y));
cout << ans << "\n";
}
Python範例代碼: