KU01 2021 Round 3
KU01 2021 Round3
จากความเดิมตอนที่แล้ว ข้อสอบในรอบที่ 1 ค่อนข้างง่ายอาจทำให้ทางผู้ออกข้อสอบคิดว่าข้อสอบรอบนี้ควรปรับให้มีความยากมากขึ้น
next
โจทย์ข้อนี้อ่านแล้วก็ค่อนข้างสับสนเพราะว่าคล้ายกับโจทย์ข้อ toi16_wheel แต่ทว่าโจทย์ข้อนี้ง่ายกว่าเพราะว่าสิ่งที่โจทย์ต้องการคือ หาการวนรอบที่ใหญ่ที่สุดเท่านั้น โดยการทำหานั้นสามารถใช้การ depth first search ได้และเก็บคำตอบไว้
int cnt = 0;
vector<int> adj[100100];
void dfs(int u){
for(auto x: adj[u]){
if(!visited[x]){
dfs(x);
}
}
cnt++;
}
และสิ่งที่เราต้องทำคือหาค่า $cnt$ ที่มากที่สุดในการ depth first search แต่ละรอบ
Time Complexiety: $O(n)$
Full Soltion in C++
#include<bits/stdc++.h>
using namespace std;
const int MxN = 100100;
vector<int> adj[MxN];
bitset<MxN> visited;
int cnt;
void dfs(int u){
for(auto x: adj[u]){
if(!visited[x]){
dfs(x);
}
}
cnt += 1;
}
int main(){
cin.tie(nullptr)->ios::sync_with_stdio(false);
int n;
cin >> n;
for(int i=1, v; i<=n; ++i){
cin >> v;
adj[i].push_back(v);
}
int ans = 0;
for(int i=1; i<=n; ++i){
if(!visited[i]){
cnt = 0;
dfs(i);
ans = max(cnt, ans);
}
}
return 0;
}
jobs
ในข้อนี้เราจะจับคู่ค่าที่มากกว่า $18$ กับค่าที่น้อยกว่าหรือเท่ากับ $18$ ให้อยู่ติดกันเพื่อให้ได้จำนวนวันที่น้อยที่สุด
Time Complexity: $O(n)$
Full Solution in C++
#include<bits/stdc++.h>
using namespace std;
int main(){
cin.tie(nullptr)->ios::sync_with_stdio(false);
int days = 0, n, minn, maxx;
cin >> n;
for(int i=1, x; i<=n; ++i){
cin >> x;
if(x > 18){
maxx++;
}
else{
minn++;
}
}
days = min(maxx, minn) * 2;
minn -= days / 2;
maxx -= day2 / 2;
if(minn > 0){
days += min;
}
if(maxx > 0){
days += (maxx - 1) * 2 + maxx;
}
cout << days;
return 0;
}
newyearlight
เมื่อเรานิยาม “กฎ” ขึ้นใหม่ให้เข้าใจได้ง่ายขึ้นว่า $rule[i]$ คือ จำนวนเส้นเชื่อมที่ใช้ในกฏข้อที่ $i$ เราจะสังเกตได้ว่าเมื่อทำ Topological Sort แล้วก็สามารถหาคำตอบได้เลย
Time Complexity: $O(n + m)$
Full Solution in C++
#include<bits/stdc++.h>
using namespace std;
const int MxN = 100100;
vector<pair<int, int>> adj[MxN];
int rule[MxN];
int main(){
cin.tie(nullptr)->ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for(int i=1, x, p; i<=n; ++i){
cin >> x;
rule[i] = x;
vector<int> v;
while(x--){
cin >> a;
v.push_back(a);
}
cin >> p;
for(auto x: v){
adj[x].emplace_back(p, i);
}
}
set<int> st;
queue<int> q;
q.push(1);
st.insert(1);
while(!q.empty()){
int now = q.front();
q.pop();
st.insert(now);
for(auto x: adj[now]){
if(--rule[x.second] == 0 && !st.count(x.first)){
st.insert(x.first);
q.push(x.first);
}
}
}
cout << st.size();
return 0;
}
Pieces
จากโจทย์เราตัดเลขขนาด $10^9 \times 10^9$ ออกเป็น $(n + 1)(m + 1)$ ชิ้นทำให้เราจำเป็นต้องใช้ unordered_map ในการเก็บตัวเลขนั้น
จากนั้นเราจะทำการนับขนาดที่เท่ากับ $a$
long long a, ans = 0;
cin >> a;
for(auto x: mpx){
if(a % x.first == 0){
ans += x.second * (mpy[a / x.first]);
}
}
cout << ans;
แต่เราจะสังเกตว่าวิธีนี้จะทำงานใน $O(n)$ เราสามารถ Optimeze เวลาให้เหลือ $O(\sqrt{n})$ ได้
Time Complexity: $O(qn)$
Full Solution in C++
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
cin.tie(nullptr)->ios::sync_with_stdio(false);
int n, m, w, l, q, x;
cin >> l >> w >> n >> m >> q;
int last = 0;
unordered_map<ll, ll> mp;
for(int i=1, x; i<=n; ++i){
cin >> x;
mpx[x - last] += 1;
last = x;
}
mpx[l - last] += 1;
last = 0;
for(int i=1; i<=m; ++i){
cin >> x;
mpy[x - last] += 1;
last = x;
}
mpy[w - last] += 1;
while(q--){
ll a, ans = 0;
cin >> a;
for(int i=1; i*i<=a; ++i){
if(a % i == 0){
ans += mpx[i] * mpy[a / i];
ans += mpy[i] * mpx[a / i];
}
}
ll sq = sqrt(a);
if(sq * sq == a){
ans -= mpx[sq] * mpy[sq];
}
cout << ans;
}
return 0;
}

author