1 条题解
-
0
自动搬运
来自洛谷,原作者为

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
- 上传者