IceBorworntat's Blog

competitive coder

KU01 2021 Round 3

Saturday, November 13, 2021, 10:22 AM

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;
}

IceBorworntat

author