nowcoder19778 游戏

链接

点击跳转

题解

维护一个并查集,每次一个人上线,我就暴力把这个人所在的集合根其相邻且在线的人的集合给合并

一个人下线,就直接把他所在的并查集的 s i z e size--

这样不好的一点在于,每次的代价都是 O ( d e g r e e ( x ) ) O(degree(x)) 的,如果他总是操作一个度数很大的点,就肯定会 T T

由于所有的点的度数之和等于 2 m 2m ,所以度数大于 2 m \sqrt {2m} 的点的个数不会超过 2 m \sqrt {2m}

度数小的我仍然可以使用暴力做法

但是度数大的,我考虑下能不能优化

注意到我们加入一个点的时候:我要把在图上和这个点相邻的而且在线的人都和自己合并

突破点就在这个"在线"

因为每次操作只能造成一个点从不在线变成在线,这就给人一种势能分析的感觉,有些需要势能分析的题目,我们总是在一些操作中积累势能,在某些操作中释放势能。现在我要处理这些度数大的点,就需要找一种“释放势能”的操作。

显然,我要对每个度数大的点记录所有影响到他的修改。也就是,和他在图上相邻的点,如果被激活了,就会影响到他,我们记录这个影响,说的明白一点,对每个度数大的点记录被激活的邻点。

当这个度数大的点一直在沉睡的时候,我就记录那些上线的临点。在度数大的点突然醒来的那一刻,我就要考虑处理这些信息了。显然我只要挨个进行并查集合并就行了,然后把这些信息丢掉(必须丢掉才能释放势能)。

具体一点:当小点上线时,我就暴力处理。同时看一下他是否影响到了某些沉睡中的大点,如果影响了就记录一下,小点上线的复杂度是 O ( d e g r e e ( u ) ) = O ( m ) O(degree(u)) = O( \sqrt {m} ) 的。大点苏醒时,先处理前面积累的信息(势能分析得到这一部分是 O ( n ) O(n) 的),这个时候我其实已经处理完了所有“在线小点”,接下来我再看一下这个大点的苏醒给其它沉睡的大点有没有影响,因为大点的个数是 O ( m ) O(\sqrt {m}) 的,所以这部分的复杂度还是 O ( m ) O(\sqrt m)

上面还只是说了上线,下线怎么处理呢?

根据最初描述的并查集做法,下线就直接把当前的并查集 s i z e size-- 就行了,其实就相当于留了个空壳在那个并查集的树上。

为了保证答案的正确性,再次回来时我可以创建一个新的节点,再开始合并,但是这样的话我处理大点的时候就不能把处理过的信息丢掉,因为处理过的信息在创建新的节点时都丢失了,必须重新合并,那这样复杂度岂不是又升上去了?

很烦,怎么解决呢。

首先想到,能不能“钻回空壳”?这样显然是不对的,举个例子 1 2 3 1-2-3 这条链,上线的顺序是 1 , 2 , 3 1,2,3 ,显然现在并查集中 1 , 2 , 3 1,2,3 是一个队伍,然后 2 , 3 2,3 下线, 3 3 上线,这个时候显然 3 3 是自己一个人一支队伍,如果我“钻回空壳”,他就会变成和 1 1 同一队伍。

但是我又猜想,是不是说,只有在相邻的朋友都掉线了的之后,“钻回空壳”才是错的?

想了一会感觉这个猜想没什么问题,应该是对的,因为只要有一个朋友在线,我即使创建了新的并查集节点,也是去和这个朋友连边,而旧的空壳本身就是参加了这个朋友所在的队伍,所以感觉信息是正确的。

那么我就再多维护一个东西:在线的朋友数量

具体就不多说了,说到这里应该思路都介绍全了

代码

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 200010
#define maxe 200010
#define maxs 350
#define cl(x) memset(x,0,sizeof(x))
#define rep(i,a,b) for(i=a;i<=b;i++)
#define drep(i,a,b) for(i=a;i>=b;i--)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
struct UnionFind
{
	ll f[maxn], size[maxn];
	void init(ll n)
	{
		for(auto i=1;i<=n;i++)f[i]=i, size[i]=0;
	}
	ll find(ll x){return f[x]==x?x:f[x]=find(f[x]);}
	void merge(ll x, ll y)
    {
        auto fx=find(x), fy=find(y);
        f[fx]=fy;
        if(fx!=fy)size[fy]+=size[fx];
    }
}uf;
struct Graph
{
    int etot, head[maxn], to[maxe], next[maxe], w[maxe];
    void clear(int N)
    {
        for(int i=1;i<=N;i++)head[i]=0;
        etot=0;
    }
    void adde(int a, int b, int c=0){to[++etot]=b;w[etot]=c;next[etot]=head[a];head[a]=etot;}
    #define forp(_,__) for(auto p=__.head[_];p;p=__.next[p])
}G;
vector<ll> online_neighbour[maxn], to_big[maxn];
ll bigtot, deg[maxn], S=316, isbig[maxn], isonline[maxn], roomtot, room[maxn], cnt[maxn];
int main()
{
    ll i, u, v, N, M, Q, T=read(), j, kase;
    rep(kase,1,T)
    {
        printf("Case #%lld:\n",kase);
        N=read(), M=read(), Q=read();
        G.clear(N), bigtot=roomtot=0;
        rep(i,1,N)isonline[i]=0, isbig[i]=0, to_big[i].clear(), online_neighbour[i].clear(), deg[i]=0, cnt[i]=0;
        rep(i,1,M)
        {
            u=read(), v=read();
            deg[u]++, deg[v]++;
            G.adde(u,v), G.adde(v,u);
        }
        rep(i,1,N)
            if(deg[i]>S)
            {
                isbig[i]=1;
            }
        rep(i,1,N)
                forp(i,G)
                    if( isbig[ G.to[p] ] )
                        to_big[i].emb(G.to[p]);
        rep(i,1,N)room[i]=i; roomtot=N;
        uf.init(Q+N);
        while(Q--)
        {
            char s[3];
            scanf("%s",s);
            ll guy = read();
            if(s[0]=='i')
            {
                if(isonline[guy])continue;
                isonline[guy]=1;
                ll x=cnt[guy];
                for(auto to:to_big[guy])if(isonline[to])x++;
                if(x==0)room[guy] = ++roomtot;
                uf.size[ uf.find(room[guy]) ]++;
                if(isbig[guy])
                {
                    for(auto x:online_neighbour[guy])
                    {
                        if(isonline[x])uf.merge(room[guy],room[x]);
                    }
                    online_neighbour[guy].clear();
                }
                else
                    forp(guy,G)
                    {
                        if(isonline[G.to[p]])
                        {
                            uf.merge(room[guy],room[G.to[p]]);
                        }
                        cnt[G.to[p]]++;
                    }
                for(auto x:to_big[guy])
                {
                    online_neighbour[x].emb(guy);
                    cnt[x]++;
                }
            }
            if(s[0]=='o')
            {
                if(isonline[guy]==0)continue;
                isonline[guy]=0;
                uf.size[ uf.find(room[guy]) ] --;
                if(isbig[guy]==0)forp(guy,G)cnt[G.to[p]]--;
            }
            if(s[0]=='q')
            {
                if(isonline[guy])printf("%lld\n",uf.size[ uf.find(room[guy]) ]);
                else printf("0\n");
            }
        }
    }
    return 0;
}
发布了948 篇原创文章 · 获赞 77 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/105011972
今日推荐