Trips
题目大意:
共有n(2e5)个人,刚开始都不认识,有m(2e5)天,第 i 天xi和yi认识了,认识不会传递,也就是a认识b,b认识c,a不一定认识c。这些人每天都想去旅游,如果每个人有他的k个朋友去旅游他才去。问每天最多去多少人。
分析一下。。
可以看成一个图,刚开始是空图,没有边,然后一次连接一个边,如果一个子图中的点要去那么在这个子图中的每个点,在这个子图中的度数都大于等于k。
就想到这里想不出来了。。
一种思想:
倒过来,就是先把所有边加上,然后找个答案就是m天的答案,然后依次删除边,再找答案。
怎么找答案呢? 刚开始 度数小于k的人肯定不行,加到队列里,然后bfs让与它相连的点的度数减1,然后 如果这个点的度数小于k就继续。。
如果当前状态不行的点,因为是删除边,所以以后的状态都肯定不行了。
因为是删除边,如果用普通的前向星建图没办法删除,(遍历一遍?肯定不行) 所以可以用set存图。
代码:
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
set<int> ss[maxn];
int du[maxn];
int ans[maxn];
int x[maxn];
int y[maxn];
int sum = 0;
int vis[maxn];
queue<int> qq;
int k;
void bfs()
{
while(!qq.empty())
{
int xx = qq.front();
qq.pop();
set<int> :: iterator it = ss[xx].begin();
for (;it != ss[xx].end(); it ++ )
{
int v = *it;
if(vis[v])
continue;
du[v] -- ;
if(du[v] < k)
{
qq.push(v);
vis[v] = 1;
sum -- ;
}
}
}
}
int main()
{
int n,m;
scanf("%d%d%d",&n,&m,&k);
for (int i= 1; i<= m; i ++ )
{
scanf("%d%d",&x[i],&y[i]);
du[x[i]] ++ ;
du[y[i]] ++ ;
ss[x[i]].insert(y[i]);
ss[y[i]].insert(x[i]);
}
sum = n;
for (int i = 1; i <= n; i ++ )
{
if(du[i] < k)
{
sum -- ;
qq.push(i);
vis[i] = 1;
}
}
bfs();
ans[m] = sum;
for(int i= m; i > 1; i -- )
{
int xx = x[i];
int yy = y[i];
if(vis[xx] == 0 && vis[yy] == 0)
{
ss[xx].erase(yy);
ss[yy].erase(xx);
du[xx] -- ;
du[yy] -- ;
if(du[xx] < k)
{
sum -- ;
vis[xx] = 1;
qq.push(xx);
}
if(du[yy] < k)
{
sum -- ;
vis[yy] = 1;
qq.push(yy);
}
bfs();
}
ans[i - 1] = sum;
}
for (int i= 1; i <= m; i ++ )
{
printf("%d\n",ans[i]);
}
// printf("\n");
}