分析:
肥肠简单但却很容易想岔的题、、、(就是那种想清楚过后觉得水得不行,然而却想了半个小时的题)
首先,这题第一个坑,就是在线/离线
因为每次边是一条一条加的,这非常类似一些数据结构的特色(单一修改),所以很容易就去想在线怎么做。。。。然而我想了至少20分钟还是没个优秀的算法。。。所以就不得不考虑离线
首先要提及一点:这个题说要去的人数最大化,但其实每个人之间并没有矛盾关系,所以只要能去的人都去就行了,把这种人定义为“合法”。
加边改删边,发现这个题删边很容易实现:每次删除后,如果这两个点都是合法的,那么这两个点的度数都-1,再检查其剩下的度数是否比k小,如果是就把这个点删去,然后与它相连的点度数-1,如此递归地操作下去。
这样均摊下来的复杂度是O(n+m)O(n+m)的(每个点最多从“合法”变为“不合法”一次)。
现在问题转化为:求连完所有边后,哪些点合法?
这就是第二个坑点,如果你要检查是否合法,也是几乎不可行的。正确的姿势是:把所有度数小于k的点删去,和上面的操作几乎一模一样。这样一来,剩下的图中,每个点连的边都一定是合法的,并且度数都不小于k。所以就得到最终状态下(连完所有边后)的合法情况了。
代码是博主在考场上写的,略丑
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#define SF scanf
#define PF printf
#define MAXN 200010
using namespace std;
typedef long long ll;
int n,m,k;
vector<int> a[MAXN],used[MAXN];
pair<int,int> l[MAXN];
set<pair<int,int> > st;
int d[MAXN];
bool del[MAXN];
int ansx[MAXN],ans;
void dele(int x){del[x]=1;ans--;for(int i=0;i<a[x].size();i++){int u=x,v=a[x][i];if(u>v)swap(u,v);if(del[a[x][i]]==0&&st.count(make_pair(u,v))==0){st.insert(make_pair(u,v));d[a[x][i]]--;if(d[a[x][i]]<k){dele(a[x][i]);}}}
}
int main(){SF("%d%d%d",&n,&m,&k);int u,v;for(int i=1;i<=m;i++){SF("%d%d",&u,&v);a[u].push_back(v);a[v].push_back(u);d[u]++;d[v]++;l[i]=make_pair(u,v);}ans=n;for(int i=1;i<=n;i++)if(d[i]<k&&del[i]==0)dele(i);for(int i=1;i<=n;i++)a[i].clear();st.clear();for(int i=1;i<=m;i++)if(del[l[i].first]==0&&del[l[i].second]==0){a[l[i].first].push_back(l[i].second);a[l[i].second].push_back(l[i].first);}for(int i=1;i<=n;i++)d[i]=a[i].size();for(int i=m;i>=1;i--){ansx[i]=ans;if(del[l[i].first]==0&&del[l[i].second]==0&&st.count(make_pair(min(l[i].first,l[i].second),max(l[i].first,l[i].second)))==0){st.insert(make_pair(min(l[i].first,l[i].second),max(l[i].first,l[i].second)));if(d[l[i].first]>d[l[i].second])swap(l[i].first,l[i].second);d[l[i].first]--;d[l[i].second]--;if(d[l[i].first]<k)dele(l[i].first);if(d[l[i].second]<k&&del[l[i].second]==0)dele(l[i].second);}}for(int i=1;i<=m;i++)PF("%d\n",ansx[i]);
}