码迷,mamicode.com
首页 > 其他好文 > 详细

题解 loj #3524. 「IOI2021」钥匙

时间:2021-06-28 18:02:51      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:second   起点   连通   获得   最小值   但我   一个   log   http   

题意

考虑暴力,以每个点为起点走下去,看能到达多少个点,最后返回每个点可到达的点是否是最小值即可。可以获得 \(37pts\)

发现如果要求每个点能到达多少个点是没法做的,但我们根本不需要求这个,我们只需要求是否是最小值就可以了。

观察性质,如果从 \(x\) 出发可以到达 \(y\) ,那么\(p_x \geq p_y\) 。如果严格大于,那么必须 \(x\) 能到 \(y\) 但是 \(y\) 不能回到 \(x\) ,这时 \(x\) 一定不是最小值,可以把它扔了。

接着发现如果 \(x\) 不合法,那么所有可以到达 \(x\) 的都不合法,都可以扔掉。

于是假设我们维护了一堆强连通分量,那么每个强连通分量里面如果有一个不合法的这一整个就废了。

考虑这样一个过程:我们不断扩展点,假设原来的点是 \(u\) ,新扩展的点是 \(v\) 。如果 \(v\) 不合法,那么把 \(u\) 所在的强连通分量废掉;如果 \(v\) 恰好被扩展过了,说明可以把 \(v\) 并到 \(u\) 所在的强连通分量里面;如果 \(v\) 没扩展过,那就考虑它能不能回来,判断它是否有用。

这样最后没用的点都会被删掉,只留下有用的。合并的时候可能要用并查集启发式合并或者线段树启发式合并,复杂度应该是 \(O((n+m)\log n)\) 的。(代码还没写,明天早上起来写。)

先放一下暴力代码吧。

#include <vector>
#include "keys.h"
using namespace std;
const int N=1000005;
vector<pair<int,int> > g[N];
vector<int> e[N];
int p[N],q[N];
bool vs[N],usd[N];
std::vector<int> find_reachable(std::vector<int> r, std::vector<int> u, std::vector<int> v, std::vector<int> c) {
	std::vector<int> ans(r.size(), 1);
	const int n=r.size();int i,j,minn=1e9,top1,top2;
	for(i=0;i<u.size();++i) g[u[i]].push_back(make_pair(v[i],c[i])),g[v[i]].push_back(make_pair(u[i],c[i]));
	for(i=0;i<n;++i){
		for(j=0;j<n;++j) vs[j]=0,e[r[j]].clear(),usd[r[j]]=0;
		top1=top2=0,q[++top2]=i,vs[i]=1,usd[r[i]]=1;
		while(top1<top2){
			const int top=q[++top1];
			for(const auto&p : g[top]) (!usd[p.second])?e[p.second].push_back(p.first),0:(!vs[p.first]?vs[p.first]=1,q[++top2]=p.first:0);
			if(!usd[r[top]]){
				usd[r[top]]=1;
				for(const int&p : e[r[top]]) !vs[p]?vs[p]=1,q[++top2]=p:0;
				e[r[top]].clear();
			}
		}
		for(j=0;j<n;++j) p[i]+=vs[j];
		if(p[i]<minn) minn=p[i];
	}
	for(i=0;i<n;++i) ans[i]=(p[i]==minn);
	return ans;
}

题解 loj #3524. 「IOI2021」钥匙

标签:second   起点   连通   获得   最小值   但我   一个   log   http   

原文地址:https://www.cnblogs.com/Kylin-xy/p/ioi2021-keys.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!