1. 程式人生 > >codeforces 1037E (圖論好題)

codeforces 1037E (圖論好題)

演算法:思維!

難度:NOIP+

簡述題意:

給一幅圖,邊從0開始每天多一條邊,問每天增加邊之後能夠有多少人去旅遊。

能去旅遊的定義是隻有當聯通的點的度數都大於等於k才能去旅遊,否則都不能去旅遊。

題解:

首先,我們將題目抽象為一張無向圖,問題轉化為動態加邊(不好寫)(之前寫的一道並查集的題,也是要倒序列舉刪邊!可以去翻我的部落格),在某一時刻最多能選多少個點,s.t.被選的點中任意一點都與其他被選的點有至少k條連邊。 正向不太好做,我們可以逆向考慮(正難則反): 首先把所有的邊都加進來。顯然此時度數還小於k的點是永遠不可能對答案有貢獻了,因此要刪去,同時更新一下與它相鄰的點的度數。重複以上操作,直到所有點的度數都大於等於k。此時剩餘點的數量就是第m天時的答案。然後我們倒著列舉,每次刪邊,並重覆上述操作,然後記錄一下這一天的答案。最後注意一下正序輸出就AC啦!

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#define ll long long
#define N 200005
using namespace std;
set<int>S[N];
int cnt;
int deg[N],ans[N],del[N];
struct node
{
	int fr;
	int to;
}edg[N];
int n,m,k;
void dele(int u)
{
	if(del[u]||deg[u]>=k) return ;
	queue<int>q;
    q.push(u);
    del[u]=1;
    --cnt;
    while(!q.empty()) 
	{
        int x=q.front();
		q.pop();
        for(auto v : S[x])//C++11 吃多了!    再也不用寫迭代器了 
		{
            --deg[v];
            if(deg[v] < k && !del[v]) 
			{
                q.push(v);
                del[v]=1;
                --cnt;
            }
        }
    }
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	cnt=n;
	for(int i = 1;i <= m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		edg[i].fr=a,edg[i].to=b;
		deg[a]++,deg[b]++;
		S[a].insert(b),S[b].insert(a);
	}
	for(int i = 1;i <= n;i++)
	{
		dele(i);
	}
	ans[m]=cnt;
	for(int i = m; i >= 1; --i) 
	{
        if(!del[edg[i].fr]) --deg[edg[i].to];
        if(!del[edg[i].to]) --deg[edg[i].fr];
        S[edg[i].fr].erase(edg[i].to), S[edg[i].to].erase(edg[i].fr);
        dele(edg[i].fr), dele(edg[i].to);
        ans[i-1] = cnt;
    }
	for(int i = 1;i <= m;i++)
	{
		printf("%d\n",ans[i]);
	}
	return 0 ;
}

C++11知識點:

1、auto!!!(百度去吧)     可以不用寫迭代器啦,哈哈哈

2、unordered_map