I Interesting Computer Game
题意:
有两个数组a , b , 第i步可以从ai bi 中选取一个数,求出最多能获取几个不同的数字 。
下面直接上AC代码:
并查集+离散化
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define T int t ;cin >> t;while(t--)
using namespace std ;
typedef long long ll;
typedef unsigned long long ull;
inline ll gcd(ll a,ll b){return b == 0? a:gcd(b, a % b);}
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll Mode(ll a, ll b, ll mode) {ll sum = 1;if(mode == 1)return 0 ;while (b) {if (b & 1) {sum = (sum * a) % mode;b--;}b /= 2;a = a * a % mode;}return sum;}
const int maxn = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const double eps = 1e-11;
const ll mod = 1e9 + 7;
ll a[maxn], b[maxn];
ll pre[maxn] ,vis[maxn] ,c[maxn];
ll tot ,n,ans ;
ll find(ll x)
{
if(pre[x] == x)
return x ;
else
return pre[x] = find(pre[x]) ;
}
void merge(ll i, ll j)
{
ll x = find(i) ;
ll y = find(j) ;
if(x == y)
{
vis[x] = 1 ;//如果之前已经联通了,现在再加一条边,那么连通块的边数肯定不低于点数了,那么标记一下祖先
return ;
}
pre[x] = y ;
if(vis[x]) vis[y] =1 ;
}
void solve()
{
scanf("%lld",&n) ;
tot = 0 ;
for(int i = 1 ; i <= n ; i++){
scanf("%lld%lld",&a[i],&b[i]) ;
c[++tot] = a[i] ;
c[++tot] = b[i] ;
}
for(int i = 0 ; i <= maxn ;i++){ //并查集初始化
pre[i] = i ;
vis[i] = 0 ;
}
sort(c+1,c+1+tot) ;
for(int i = 1 ; i<= n ;i++){ //离散化
a[i] = lower_bound(c+1,c+tot+1,a[i]) - c ;
b[i] = lower_bound(c+1,c+tot+1,b[i]) - c ;
merge(a[i],b[i]) ;
}
ans = tot ;
for(int i = 1 ; i <= tot ; i++){ //注意,这里离散化已经完成,不再是1-n 而是1-tot
if(vis[i] == 0&& pre[i] == i)
ans-- ;
}
}
int main()
{
int Case;
cin >> Case;
int x = Case ;
while (Case--) {
solve();
printf("Case #%d: %lld\n",x-Case,ans) ;
}
return 0;
}
- 下面附上知识点的补充(lower_bound和upper_bound):
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。