1 条题解

  • 0
    @ 2025-8-24 22:04:25

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar smzzl
    **

    搬运于2025-08-24 22:04:25,当前版本为作者最后更新于2018-11-07 13:24:24,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这题怎么就黑了(我这么菜怎么会做得出来黑题呢)

    这题怎么做呢?其实主要是要细细读一下那两个性质

    对于1,每个自己不同的领域的点之间都要有边

    对于2,自己的领域的每一个点要和对面的每一个领域的每一个点要有边

    认真看着两个条件是不是会发现如果对面拿了一个领域,那么这个领域作为自己的领域也是满足条件的

    所以不要给走狗留领域

    好了现在问题就是怎么分配这个领域了

    显然对于两个联通块,若他们之间没有边,则要划分到同一个领域去

    所以怎么划分领域?

    对于每一个点A它必定属于一个领域,那么我们以这个点进行扩展,我们要这个领域尽量多,因此我们维护一个队列表示和当前枚举的A在不在一个领域内。对于这个A点沿着他的出边拓展,所有被A访问到的点都是允许不和A在同一个领域,所以偶没有被A访问到的点必定和A在同一个领域,这里我们就用一个桶来维护,每次将其++,把这些没有被访问到的点进入队列.然后我们要用一个limit维护这个领域大小,遍历这个桶,将遍历到的点如果他的桶的值小于limit,那么说明这个点不能被这个领域的所有点到达,因此他要属于这个领域,按照这个方法维护即可。具体细节见代码注释。

    时间复杂度为O(m+nk),k为领域数量,m为边数

    极端情况退化n^2?

    不会的,因为此题m不大,因此可以知道k不会很大,这个时间复杂度能够通过此题。若有更优秀的做法欢迎指出。

    (路过点个赞呀)

    code:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    
    struct note{
        int seq,val,act;
    }que[4000008];
    int n,m,l,maxp;
    int x[4000008],y[4000008],_last[4000008],_next[4000008];
    int vis[4000008],cnt[4000008],fa[4000008],tp[4000006];
    
    inline void edge(int u,int v){
        l++; x[l]=u; y[l]=v; _next[l]=_last[u]; _last[u]=l;
    }
    inline int _max(int a1,int a2){if (a1<a2) return a2; else return a1;}
    bool cmp(int a1,int a2){return a1<a2;}
    
    inline void dividing(int);
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (register int i=1;i<=n;i++) _last[i]=-1;
        for (register int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            edge(u,v),edge(v,u);
        }
        for (register int i=1;i<=n;i++)
            if (!vis[i]) dividing(i);
        printf("%d\n",maxp);
        sort(cnt+1,cnt+1+maxp,cmp);
        for (register int i=1;i<=maxp;i++) printf("%d ",cnt[i]);
    }
    
    inline void dividing(int pos)
    {
        
        for (register int i=1;i<=n;i++) fa[i]=i,tp[i]=vis[i];
        register int limit=0,lef=1,rig=n,res=n,deep=n,spp=1;
        for (register int i=1;i<=n;i++) que[i].seq=i,que[i].val=0;//inti
        while (res && lef<=deep && deep<=10*n)
          if (que[lef].val<limit || spp)//若为第一个或者这个点的桶值没有到达limit
            {
                pos=que[lef].seq; lef++; 
                if (vis[pos]) continue; 
                vis[pos]=1;
                for (register int j=_last[pos];j!=-1;j=_next[j]){
                	int tar=y[j];
                	while (fa[tar]!=0 && fa[tar]!=tar) tar=fa[tar];
                    que[tar].val++;
                }
                limit++;
                res--; spp=0;
            }else {
                if (que[lef].val==999999999) {lef++; continue;}
                rig++;
                fa[que[lef].seq]=rig;
                que[rig]=que[lef];
                que[lef].val=999999999;
                lef++;
    			deep++;//遍历出边,更新最大deep(因为有下面的移动操作,不能到n就停
                //这个点到目前为止成立并不代表完全成立,要将他移动到队列末尾,fa数组维护他的新位置。
            }
        maxp++;
        for (register int i=1;i<=n;i++) 
            if (tp[i]!=vis[i]) cnt[maxp]++;//统计答案
    }
    }
    

    (直接复制抄袭会ce) (bug不影响阅读) (这是重发的之前那个有一点bug)

    • 1

    信息

    ID
    2911
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者