[SDOI2019] 热闹的聚会与尴尬的聚会

Posted ysfac

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2019] 热闹的聚会与尴尬的聚会相关的知识,希望对你有一定的参考价值。

题意:

你有n个好友,他们之间有m对关系$(u,v)$表示u和v互相认识,认识没有传递性。

现在你想组织一场热闹的聚会和一场尴尬的聚会,定义如下:

  • 一场热闹度为p的聚会请来了任意多位好友,对于每一位到场的好友来说都有至少p位他认识的好友也参加了聚会,且至少对于一位到场的好友来说现场恰好有p位他认识的好友;
  • 一场尴尬度为q的聚会请来了恰好q位好友,且他们两两互不认识。

这两场聚会之间没有任何联系。

你希望p和q满足$lfloor frac{n}{p+1} floor leq q$且$lfloor frac{n}{q+1} floor leq p$,求一种合法方案。

$Tleq 32,nleq 10000,mleq 10^{5}$。

 

题解:

首先发现对于任意p/q,q/p越大越优,所以原问题可以拆成求最大的p和最大的q。

考虑求最大的p,显然可以依次尝试删除图中度数最小的点,尽管删除点数越多答案不一定越优,但记录最大答案及其删除个数即可。

考虑求最大的q,贪心的想法是依次取图中度数最小的点,并删除它和所有与它相邻的点。

但显然这样求出的并不是最大独立集。因为求一般图最大独立集是个NP问题。

不过,我们本身要求的并不是最大的q,而是任意一个满足$lfloor frac{n}{p+1} floor leq q$且$lfloor frac{n}{q+1} floor leq p$的q,所以有一个奇妙的结论:这样贪心是100%正确的。

证明:考虑p算法的过程,我们每次删除的点的度数一定$leq p$。

而q算法删的点的度数比p只少不多,也就是说对于q中每次操作至多会删掉p+1个点。

那么显然q算法至少会运行$lceil frac{n}{p+1} ceil$次,这也对应着该算法求出的q的下界。

于是求出的p和q一定满足题目中的限制条件($(p+1)(q+1)>n$时两个条件均满足),证毕。

复杂度$O(T(n+m)logn)$(大概吧),需要稍加优化。

(也许运行q算法时直接删点不更新边权也是对的,但我并不想证明)

 

套路:

  • 在有某些限制条件时瞎蒙一个贪心很可能是对的;
  • 构造题可以考虑将若干个限制条件合并成一个较宽松的限制条件,一般很少有人喜欢把下界卡死(大概吧)。

 

代码:

技术图片
#include<bits/stdc++.h>
#define maxn 10005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define mp make_pair
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
int to[maxm],nxt[maxm],hd[maxn],vis[maxn],cnt;
int A[maxn],B[maxn],deg[maxn],D[maxn];
priority_queue<pair<int,int> > Q;
map<pair<int,int>,int> M;

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==-) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-0;
    return x*f;
}

inline void addedge(int u,int v){
    to[++cnt]=v,nxt[cnt]=hd[u],hd[u]=cnt;
    to[++cnt]=u,nxt[cnt]=hd[v],hd[v]=cnt;
}

int main(){
    int T=read();
    while(T--){
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(hd,0,sizeof(hd));
        memset(deg,0,sizeof(deg));
        memset(vis,0,sizeof(vis));
        M.clear(),cnt=0;
        while(!Q.empty()) Q.pop();
        int n=read(),m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read(); if(u>v) swap(u,v);
            if(M[mp(u,v)]) continue;
            else addedge(u,v),M[mp(u,v)]=1,deg[u]++,deg[v]++;
        }
        memcpy(D,deg,sizeof(deg));
        for(int i=1;i<=n;i++) Q.push(mp(-deg[i],i));
        int ans=0,endpos=0;
        while(!Q.empty()){
            int u=Q.top().second,d=-Q.top().first; 
            Q.pop(); if(vis[u]) continue;
            vis[u]=1,A[++A[0]]=u;
            if(ans<-Q.top().first)
                ans=-Q.top().first,endpos=A[0];
            for(int i=hd[u];i;i=nxt[i])
                if(!vis[to[i]]) 
                    Q.push(mp(-(--deg[to[i]]),to[i]));
            
        }
        memcpy(deg,D,sizeof(D));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) Q.push(mp(-deg[i],i));
        while(!Q.empty()){
            int u=Q.top().second,d=-Q.top().first; 
            Q.pop(); if(vis[u]) continue;
            vis[u]=1,B[++B[0]]=u;
            for(int i=hd[u];i;i=nxt[i]){
                int v=to[i];
                if(vis[v]) continue;
                vis[v]=1;
                for(int j=hd[v];j;j=nxt[j]){
                    int w=to[j];
                    if(vis[w]) continue;
                    Q.push(mp(-(--deg[w]),w));
                }
            }
        }
        printf("%d",n-endpos);
        for(int i=endpos+1;i<=n;i++) printf(" %d",A[i]);
        printf("
");
        printf("%d",B[0]);
        for(int i=1;i<=B[0];i++) printf(" %d",B[i]);
        printf("
");
    }
    return 0;
}
热闹的聚会与尴尬的聚会

 

以上是关于[SDOI2019] 热闹的聚会与尴尬的聚会的主要内容,如果未能解决你的问题,请参考以下文章

[SDOI2019] 热闹又尴尬的聚会

[SDOI2019]热闹又尴尬的聚会(图论+set+构造)

洛谷P5361热闹的聚会与尴尬的聚会

国庆回家,同学聚会程序员的尴尬

聚会的快乐

树形动态规划 fjutoj-2392 聚会的快乐