求最小值最大显然是要二分
二分之后转换成了判定性问题
我们考虑哪些点一定不能选
显然是将所有可选点选中之后依然不满足条件的点不能选
那么我们不妨维护一个堆,每次取出堆顶看看是否满足条件
不满足条件就pop掉,并进行松弛
最后判定堆是否为空即可
另外,其实这道题思考到这里我们会发现二分并没有什么卵用,可以去掉二分省掉一个log
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cstdlib> #include<queue> using namespace std; const int maxn=100010; int n,m,k,x,u,v,tot; bool vis[maxn]; bool check[maxn]; int deg[maxn]; int sum[maxn]; int ans[maxn]; int h[maxn],cnt=0; struct edge{ int to,next; }G[maxn<<1]; struct pos{ double k;//���� int now;//��ǰ�� pos(double k=0,int now=0):k(k),now(now){} bool operator <(const pos &A)const{ return k>A.k; } }; priority_queue<pos>Q; void add(int x,int y){++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;} bool Get_check(double k){ memset(check,0,sizeof(check)); memset(deg,0,sizeof(deg)); while(!Q.empty())Q.pop(); for(int u=1;u<=n;++u){ if(vis[u])continue; for(int i=h[u];i;i=G[i].next){ int v=G[i].to; if(vis[v])continue; deg[v]++; } } for(int i=1;i<=n;++i){ if(vis[i])continue; Q.push(pos((double)(deg[i])/sum[i],i)); } while(!Q.empty()){ while(!Q.empty()&&check[Q.top().now])Q.pop(); if(Q.empty())break; pos tmp=Q.top(); if(tmp.k>=k)break; int u=tmp.now;check[u]=true; for(int i=h[u];i;i=G[i].next){ int v=G[i].to; if(check[v]||vis[v])continue; deg[v]--; Q.push(pos((double)(deg[v])/sum[v],v)); } } if(Q.empty())return false; return true; } int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=k;++i){ scanf("%d",&x); vis[x]=true; } for(int i=1;i<=m;++i){ scanf("%d%d",&u,&v); add(u,v);add(v,u); sum[u]++;sum[v]++; } double L=0,R=1; for(int i=1;i<=50;++i){ double mid=(L+R)/2; if(Get_check(mid))L=mid; else R=mid; } Get_check(L);tot=0; for(int i=1;i<=n;++i){ if(check[i]||vis[i])continue; ans[++tot]=i; } printf("%d ",tot); for(int i=1;i<=tot;++i)printf("%d ",ans[i]); return 0; }