题目链接:http://codeforces.com/contest/1243/problem/D
翻译:给n个点m条边(1<=n<=1e5,0<=m<=min(n*(n-1)/2,1e5)),构成一张无向图,给的m条边上,边权均为1,在原有m条边的基础上再添加边权为0的边把图变成完全图,这时候问,构成最小生成树最小路径。
思路:就是联通图,有边权为0的两个点可以缩成一个点,生成树答案就是连通图个数减一。那么,连通图怎么找呢,要保证被遍历过的点不能再影响到时间复杂度,这就是这题的关键,那么如何保证被遍历过的点不再能影响时间复杂度呢,这时候,只需要用一个容器把所有未访问的点存起来,再用这些点来进行遍历,每个点能访问到未访问的点先用容器存起来,再把这些点从存未访问点的大容器中删掉,然后再遍历小容器遍历这些点。这样,找联通图的复杂度的规模就被缩小到n了,即1e5。
我的代码使用了map来存边,所以时间复杂度上升到了nlogn不过并不关键,一般来说map是不如unordered_map,但是unordered_map也可以被卡成O(n),那样也可能T,所以使用map能保证红黑树的复杂度是logn,更加稳妥
ac代码:
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#include <cstring>
#include <set>
#include <map>
#include <algorithm>
#define ms(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
using namespace std;
const int maxn =1e5+5;//提交记得修改
set<int>s;//用于存放未访问的边
map<int,bool>mp[maxn];
int n,m;
bool vis[maxn];
void dfs(int u){
vis[u]=true;
set<int>::iterator it;
vector<int>v;
for(it=s.begin();it!=s.end();it++)if(!mp[u][*it])v.push_back(*it);
//若是被map存了的边,则不加到容器中
int sz=v.size();
for(int i=0;i<sz;i++)s.erase(v[i]);
for(int i=0;i<sz;i++)dfs(v[i]);
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);//提交记得注释
scanf("%d%d",&n,&m);
while(m--){
int u,v;
scanf("%d%d",&u,&v);
mp[u][v]=mp[v][u]=true;
}
for(int i=1;i<=n;i++)s.insert(i);
int ans=0;
for(int i=1;i<=n;i++)if(!vis[i])ans++,dfs(i);
printf("%d\n",ans-1);
}