zoukankan      html  css  js  c++  java
  • 并不对劲的loj3113:p5360:[SDOI2019]热闹的聚会与尴尬的聚会

    题目大意

    有个(n)个点(m)条边的图。
    要找到一个最小度数为(p)的子图和(q)个互不相连的点,使(pgeqlfloorfrac{n}{q+1} floor)(qgeqlfloorfrac{n}{p+1} floor)
    (nleq 10^4;mleq 10^5;数据组数leq32;)

    题解

    题目的条件中把分母乘过去,会发现需要(p imes q)尽可能大。
    (p,q)的大小看上去没什么关系,就相当于让它们分别尽可能大。
    使(p)尽可能大:发现只有删掉当前的图中度数最小的点,才能使最小度数增加。所以每次删掉当前图中度数最小的点,并更新度数,记下最大的最小度数出现在第几次。
    使(q)尽可能大:“最大点独立集”是个np问题,但这题没必要“最大”,只要大于(lfloorfrac{n}{p+1} floor)就行。可以每次选出度数最小的点加入点集,删去与它相连的点。如果删去的点数(即度数最小的点的度数)大于(p),那么删之前所有点的度数都大于(p),那么(p)就不是最大的了。之前第一问的做法保证了(p)最大,所以每次删的点数不大于(p),加入点集的点数(q imes (p+1)geq n)
    这两问中用可删堆容易超时,发现两问中都是一个点只会被删一次,开数组记录已经被删的点就行了。

    代码
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<iomanip>
    #include<iostream>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<vector>
    #define rep(i,x,y) for(register int i=(x);i<=(y);++i)
    #define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
    #define view(u,k) for(int k=fir[u];~k;k=nxt[k])
    #define maxn 10007
    #define maxm 200007
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mp make_pair
    using namespace std;
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)&&ch!='-')ch=getchar();
    	if(ch=='-')f=-1,ch=getchar();
    	while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    	return x*f;
    }
    void write(int x)
    {
    	if(x==0){putchar('0'),putchar(' ');return;}
    	int f=0;char ch[20];
    	if(x<0)putchar('-'),x=-x;
    	while(x)ch[++f]=x%10+'0',x/=10;
    	while(f)putchar(ch[f--]);
    	putchar(' ');
    	return;
    }
    int t,n,m,fir[maxn],nxt[maxm],v[maxm],cnte,ord[maxn],vis[maxn],d[maxn],tp,pos,mx;
    priority_queue<pii >q;
    void ade(int u1,int v1){v[cnte]=v1,nxt[cnte]=fir[u1],fir[u1]=cnte++;}
    int main()
    {
    	t=read();
    	while(t--)
    	{
    		n=read(),m=read();
    		rep(i,1,n)fir[i]=-1,d[i]=ord[i]=vis[i]=0;cnte=mx=pos=tp=0;
    		rep(i,1,m){int x=read(),y=read();ade(x,y),ade(y,x),d[x]++,d[y]++;}
    		rep(i,1,n)q.push(mp(-d[i],i));
    		while(!q.empty())
    		{
    			int u=q.top().se,su=-q.top().fi;q.pop();if(vis[u])continue;
    			vis[u]=1;ord[++tp]=u;if(!pos||mx<su)pos=tp,mx=su;
    			view(u,k)if(!vis[v[k]]){d[v[k]]--;q.push(mp(-d[v[k]],v[k]));}
    		}
    		write(n-pos+1);rep(i,pos,n)write(ord[i]);puts("");
    		tp=0;rep(i,1,n)q.push(mp(-d[i],i)),vis[i]=0;
    		while(!q.empty())
    		{
    			int u=q.top().se;q.pop();if(vis[u])continue;
    			vis[u]=1;ord[++tp]=u;
    			view(u,k)vis[v[k]]=1;
    		}
    		write(tp);rep(i,1,tp)write(ord[i]);puts("");
    		
    	}
    	return 0;
    }
    
    奇怪的思路

    把每个点编为与周围染好的点不同的编号最小的编号。
    第一问取编号恰为1~最大编号的点,第二问取数量最多的同一编号的点(数量一定大于(lfloorfrac{n}{编号数+1} floor))。
    但是最大编号和进行编号的顺序有关,有的图能通过不巧的编号顺序增大最大编号,所以这个方法是错的(或者说我不知道能不能修)。
    这个方法只有10分,第一问无脑输出所有点(loj数据)都有80分。

  • 相关阅读:
    Linux基础命令:crontab
    linux基础命令:su和sudo
    Linux基础知识:文件权限管理以及umask
    Linux基础命令:useradd和groupadd
    Linux基础命令:more和less
    Linux基础命令:ln
    Linux基础命令:chattr和lsattr
    Linux基础命令:who和w
    vue项目1-pizza点餐系统6-路由精讲之复用router-view
    vue项目1-pizza点餐系统5-全局守卫
  • 原文地址:https://www.cnblogs.com/xzyf/p/13033835.html
Copyright © 2011-2022 走看看