first
题目大意:给一幅图,边从 0 开始每天多一条边,问每天增加边之后能够有多少人去旅游。能去旅游的定义是只有当联通的点的度数都大于等于 k 才能去旅游,否则都不能去旅游。询问每天最多能去旅游的点的数量
second
很明显正向不太好做,于是离线建边的信息,考虑反向跑:每次都删去度小于(k)的点(对点标记),删去与其相连的边(同样对边进行标号标记即可),将与其相连的还未删去的点的度修改一下,反向统计答案即可。
third
代码里有详注
(这里使用小根配对堆维护度的取出):
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define pa pair<int,int>
#define heep __gnu_pbds::priority_queue<pa,greater<pa> >
#define nm make_pair
inline int read() //读入优化
{
int x=0,f=1;char ch=getchar();
for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
}
const int xx=2e5+10;
int du[xx],cnt; //点的度与当前剩余的未被删的点的数量
vector<pa>e[xx]; //存储两个信息:连的点与边的编号
struct edge{int u,v,ans;bool use;}d[xx]; //存储边的两端,该时间的答案与该边是否被删
heep p; //小根配对堆存度与点编号
heep::point_iterator id[xx];
bool cut[xx]; //点是否被删
int main()
{
int n=in,m=in,k=in;
fur(i,1,m)
{
int x=in,y=in;
d[i].u=x,d[i].v=y;
e[x].push_back(nm(y,i));
e[y].push_back(nm(x,i));
++du[x];++du[y];
}
fur(i,1,n) id[i]=p.push(nm(du[i],i)); //将度压入堆
cnt=n; //初始化为被删的点的数量
fdr(i,m,1)
{
while(!p.empty()&&p.top().first<k)
{
int u=p.top().second;p.pop();
cut[u]=true;du[u]=0; //删
--cnt; //点
fur(j,0,(int)e[u].size()-1)
{
if(cut[e[u][j].first]) continue; //连点已被删,跳过
if(d[e[u][j].second].use) continue; //连边已被删,跳过
d[e[u][j].second].use=true; //否则删边
--du[e[u][j].first]; //修改连点的度
p.modify(id[e[u][j].first],nm(du[e[u][j].first],e[u][j].first)); //修改ing
}
}
d[i].ans=cnt; //存储答案
if(!d[i].use) //若该边没被删
{
if(cut[d[i].u]||cut[d[i].v]) continue; //若边的两端任何一点被删,跳过
--du[d[i].u]; //否则修改点的度
--du[d[i].v];
p.modify(id[d[i].u],nm(du[d[i].u],d[i].u));
p.modify(id[d[i].v],nm(du[d[i].v],d[i].v));
d[i].use=true; //且删边
}
}
fur(i,1,m) printf("%d
",d[i].ans); //正向输出答案
return 0;
}