疫情的防控-(倒叙并查集)

疫情防控L3

思考:
首先题意说给n点m边,然后k条处理每次删去一个点,然后再给你一些点对,看看是否能互相到达。明显能看出是找出点是否在一个连通块或者集合里面,如果dfs就标记,被删掉的点不能走,很简单,但是这样时间复杂度很大。另一个就会想到并查集,但是仅仅会简单的去想删边并查集也不能操作啊,记住以后只要想删边,那就去想离线倒叙并查集。每次处理最后的,然后再把点边补上,这是很好的复杂度。

结论:
直接dfs标记类简单但是复杂度高,倒叙并查集是个好的选择。

代码:

int T,n,m,k;
int va[N];
int vis[N];
int acc[N];
vector<int > e[N],re,anw;
vector<PII > v[N];

int find(int x);

signed main()
{
	IOS;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) acc[i] = i;
	for(int i=1;i<=m;i++)
	{
		int a,b;
		cin>>a>>b;
		e[a].pb(b);
		e[b].pb(a);
	}
	for(int i=1;i<=k;i++)
	{
		int A,x;
		cin>>A>>x;
		re.pb(A);
		vis[A] = 1;
		for(int j=1;j<=x;j++)
		{
			int a,b;
			cin>>a>>b;
			v[i].pb({a,b});
		}
	}
	for(int now=1;now<=n;now++)
	{
		for(auto spot:e[now])
		{
			if(vis[spot]||vis[now]) continue;
			int t1 = find(now),t2 = find(spot);
			if(t1!=t2) acc[t1] = t2;
		}
	}
	for(int i=k;i>=1;i--)
	{
		int ans = 0;
		for(auto t:v[i])
		{
			int t1 = find(t.fi),t2 = find(t.se);
			if(t1!=t2||vis[t.fi]||vis[t.se]) ans++;
		}
		int now = re[i-1];
		vis[now] = 0;
		for(auto spot:e[now])
		{
			int t1 = find(now),t2 = find(spot);
			if(t1!=t2) acc[t1] = t2;
		}
		anw.pb(ans);
	}
	for(int i=anw.size()-1;i>=0;i--) cout<<anw[i]<<"\n";
	return 0;
}

int find(int x)
{
	if(x!=acc[x]) acc[x] = find(acc[x]);
	return acc[x];
}

总结:
总言而止呢,比赛不要太紧张,思考一些傻逼性的问题是没有结果的,要冷静下来去分析如何去解决它。

卡空间:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define ll long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;
const int mod = 1e9+7,inf = 1e9;
const int N = 5e4+10,M = 2e6+10,K = 2e5+10;

int T,n,m,k;
PII va[N];
int vb[N];
int vis[N];
int acc[N];
int anw[N];

int h[K],e[K],ne[K],idx;
int h1[M],e1[M],e2[M],ne1[M],idx1;

void add(int a,int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

void add1(int a,int b,int c)
{
	e1[idx1] = b;
	e2[idx1] = c;
	ne1[idx1] = h1[a];
	h1[a] = idx1++;
}

int find(int x)
{
	if(x!=acc[x]) acc[x] = find(acc[x]);
	return acc[x];	
}

signed main()
{
	IOS;
	cin>>n>>m>>k;
	mem(h,-1);mem(h1,-1);
	for(int i=1;i<=n;i++) acc[i] = i;
	for(int i=1;i<=m;i++)
	{
		int a,b;
		cin>>a>>b;
		va[i] = {a,b};
		add(a,b);add(b,a);
	}
	for(int i=1;i<=k;i++)
	{
		int x;
		cin>>vb[i]>>x;
		vis[vb[i]] = 1;
		while(x--)
		{
			int a,b;
			cin>>a>>b;
			add1(i,a,b);
		}
	}
	for(int i=1;i<=m;i++)
	{
		int a = va[i].fi,b = va[i].se;
		if(vis[a]||vis[b]) continue;
		acc[find(a)] = find(b);
	}
	for(int i=k;i>=1;i--)
	{
		for(int j=h1[i];j!=-1;j=ne1[j])
		{
			int a = e1[j],b = e2[j];
			if(vis[a]||vis[b]||find(a)!=find(b)) anw[i]++;
		}
		vis[vb[i]] = 0;
		for(int j=h[vb[i]];j!=-1;j=ne[j])
		{
			int a = vb[i],b = e[j];
			acc[find(b)] = find(a);
		}
	}
	for(int i=1;i<=k;i++) cout<<anw[i]<<"\n";
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值