Có
Nếu game chỉ có
Nếu
Dòng
Dòng
Gồm
5 2 4
1 2 2 2 1
1 2
2 3
1 5
4 5
3
4
Với mỗi màu
Sử dụng
map <int, int> cnt[maxn];
Trường hợp đặc biệt: Nếu màu
Đối với trường hợp màu
Cách làm thông thường với DSU là với mỗi cạnh
for (auto [color, w] : cnt[v])
{
cnt[u][color] += w;
}
Sau khi hợp màu
...
cnt[u][color] += w;
if (cnt[u][w] == số đỉnh màu c ban đầu && ans[c] != -1)
ans[c] = i;
...
Trường hợp xấu nhất đối với cách làm trâu phía trên, là với mỗi cạnh, thành phần chứa
Vì vậy chúng ta sẽ chỉ hợp màu từ thành phần nhỏ hơn sang thành phần lớn hơn, như vậy độ phức tạp bộ sẽ giảm xuống chỉ còn
u = get(u); v = get(v);
if (cnt[u].size() < cnt[v].size()) swap(u, v);
for (auto [color, w] : cnt[v])
{
...
}
/*
www.youtube.com/YugiHackerChannel
oj.vnoi.info/user/YugiHackerKhongCopCode
*/
#include<bits/stdc++.h>
#define el cout<<"\n"
#define f0(i,n) for(int i=0;i<n;++i)
#define f1(i,n) for(int i=1;i<=n;++i)
#define maxn 100005
using namespace std;
int n, m, q, a[maxn];
int p[maxn], d[maxn], ans[maxn];
/// d[c] : Số đỉnh màu c trong đồ thị ban đầu
/// ans[c] : Kết quả cho màu c
map <int, int> cnt[maxn]; /// cnt[u] : Chứa tập màu c và số lượng tương ứng của màu c trong thành phần liên thông chứa u
int get(int u) {return p[u] == u ? u : p[u] = get(p[u]);}
main()
{
ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n >> m >> q;
for (int i=1; i<=n; i++)
{
cin >> a[i];
d[a[i]]++;
}
for (int i=1; i<=m; i++)
ans[i] = -1;
for (int i=1; i<=n; i++)
{
p[i] = i;
cnt[i][a[i]]++;
if (d[a[i]] == 1) ans[a[i]] = 0; /// Trường hợp chỉ có một đỉnh có màu a[i]
}
for (int i=1; i<=q; i++)
{
int u, v; cin >> u >> v;
u = get(u), v = get(v);
if (u != v)
{
if (cnt[u].size() < cnt[v].size()) swap(u, v);
p[v] = u;
for (auto [color, w] : cnt[v])
{
cnt[u][color] += w;
if (cnt[u][color] == d[color] && ans[color] == -1)
ans[color] = i; /// Nếu hợp thành phần chứa u - v lại khiến số màu c bằng số màu trong đồ thị ban đầu, thì ta lưu lại kết quả cho màu c. Lưu ý nếu ans[c] != -1, chứng tỏ trước đó màu c đã đủ trong thành phần liên thông chứa v
}
}
}
for (int i=1; i<=m; i++)
cout << ans[i] << '\n';
}
Từ thuật trâu sử dụng binary search phía trên, ta biết rằng với mỗi màu
Với code của bài này mình sử dụng style chặt nhị phân
Đầu tiên, gọi
Sau đó, ta sẽ kiểm tra các màu
Để kiểm tra mọi đỉnh có màu
Sau khi dừng việc xử lý, in ra
/*
www.youtube.com/YugiHackerChannel
oj.vnoi.info/user/YugiHackerKhongCopCode
*/
#include<bits/stdc++.h>
#define el cout<<"\n"
#define f0(i,n) for(int i=0;i<n;++i)
#define f1(i,n) for(int i=1;i<=n;++i)
#define maxn 100005
using namespace std;
int n, m, q, a[maxn], l[maxn], r[maxn];
int p[maxn];
int get(int x){return (x == p[x] ? x : p[x] = get(p[x]));}
void unite(int x, int y)
{
x = get(x); y = get(y);
if (x != y) p[x] = y;
}
struct edge
{
int u, v;
}b[maxn];
vector <int> colors[maxn];
main()
{
ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n >> m >> q;
f1 (i, n)
{
cin >> a[i];
colors[a[i]].push_back(i);
}
for (int i=1; i<=q; i++) cin >> b[i].u >> b[i].v;
for (int i=1; i<=m; i++)
{
l[i] = -1;
r[i] = q+1;
}
for(;;)
{
bool answered = 1;
vector<vector<int>> queries(q+1);
for (int i=1; i<=m; i++)
{
if (r[i] - l[i] > 1) answered = 0;
queries[(l[i]+r[i])/2].push_back(i);
}
if (answered) break;
for (int i=1; i<=n; i++) p[i] = i; /// reset DSU
for (int i=0; i<=q; i++)
{
if (i != 0)
{
int u = get(b[i].u), v = get(b[i].v);
unite(u, v);
}
for (int color : queries[i])
{
int u = get(colors[color][0]);
bool ok = 1;
for (int id : colors[color])
{
int v = get(id);
if (u != v) ok = 0;
}
if (ok) r[color] = i;
else l[color] = i;
}
}
}
for (int i=1; i<=m; ++i)
{
if (r[i] == q+1) r[i] = -1;
cout << r[i] << '\n';
}
}