I - Invoking the Magic

今天训练的时候死活都过不去的一道题目

题目其实比较好理解,就是给定n对袜子(袜子保证有且只有两只),如果像 1 2    2  3    3  1  这样的可以合并起来,求一次性最多可以弄多少双这样的袜子(

第一个想法就是开数组,比如1 2则开形如a[1] = 2的数组,然后开始循环去找下一个以2为下标的一个数字,但是遇到了两个困难,一个是数据范围(2的30次太大了数组肯定开不下),第二个则是经过队友的亲身实践,这种方法会在第3个样例t掉

那么需要有一种方法记录这些数字是连在同一组的,可以用什么方法解决这个问题呢?

那么此时的话并查集就出来了,如果他们是一组的,显而易见每一个结点之间都有一条双向边连接,同时也正因如此,他们的父亲结点是一样的,那么我们在判断的时候只需要将父亲结点一样的数字的个数相加即可

但是怎么解决2的30次呢?

此时的话一个超级神奇的stl要出现了

就是map

但是它不是普通的map,因为普通的map相对来说会超时

但是unordered_map内部存在着哈希表,相对来说查询和修改比较快,因此我们可以使用unordered_map来存储相应结点的父亲结点

#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
typedef long long ll;
unordered_map<ll,ll> ss,sp;
int getf(ll x)
{
    if(x==ss[x]) return x;
    ll temp = ss[x];
    ss[x] = getf(temp);
    return ss[x];
}
void combine(ll x,ll y)
{
    int fx=getf(x);
    int fy=getf(y);
    if(fx!=fy)
    {
        ss[fy]=fx;
    }
}
ll a[100010];
ll b[100010];
int main()
{
    int t;
    int n;
    cin>>t;
    while(t--)
    {
        ss.clear();
        sp.clear();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld %lld",&a[i],&b[i]);
        //    cout<<ss[a[i]]<<endl;
            if(ss[a[i]]==0) ss[a[i]]=a[i];
            if(ss[b[i]]==0) ss[b[i]]=b[i];
        //    cout<<"getf"<<getf(a[i])<<" "<<getf(b[i])<<endl;
            combine(ss[a[i]],ss[b[i]]);
        //    cout<<"father:"<<ss[a[i]]<<" "<<ss[b[i]]<<endl;
        }
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            sp[getf(ss[a[i]])]++;
            ans=max(ans,sp[getf(ss[a[i]])]);
        //    cout<<sp[ss[a[i]]]<<endl;
            sp[getf(ss[b[i]])]++;
            ans=max(ans,sp[getf(ss[b[i]])]);
        }
        printf("%lld\n",ans>>1);
    }
    return 0;
} 

tips:注意及时对map清空 不然数据的残留会对此有一定的影响  

posted @ 2021-04-03 21:35  Treasure-  阅读(200)  评论(0)    收藏  举报