• 题解 loj #3524. 「IOI2021」钥匙


    题意

    考虑暴力,以每个点为起点走下去,看能到达多少个点,最后返回每个点可到达的点是否是最小值即可。可以获得 (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)) 的。(代码还没写,明天早上起来写。)

    先放一下暴力代码吧。

    upd : 代码写了,但是写的时候出了一堆锅,最后还借鉴了别人的代码。真的就水平不行啊。

    37pts
    #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;
    }
    
    100pts
    #include <vector>
    #include <set>
    #include "keys.h"
    using namespace std;
    const int N=1000005;
    set<pair<int,int> > g[N];
    set<int> have[N];
    vector<int> reach[N];
    int n,m,fa[N],sz[N],in[N],tag[N],sta[N],top;
    void ckMin(int &p,int q){p=(p<q?p:q);}
    int fd(int x){return fa[x]^x?fa[x]=fd(fa[x]):x;}
    void mer(int u,int v){
    	u=fd(u),v=fd(v);
    	if(g[u].size()+have[u].size()+reach[u].size()>g[v].size()+have[v].size()+reach[v].size()) std::swap(u,v);
    	fa[u]=v,sz[v]+=sz[u];
    	for(const auto&p : reach[u]) reach[v].push_back(p);
    	reach[u].clear();
    	for(const auto&p : have[u]){
    		auto pos=g[v].lower_bound(make_pair(p,0));
    		while(pos!=g[v].end()&&pos->first==p) reach[v].push_back(pos->second),pos=g[v].erase(pos);
    		have[v].insert(p);
    	}
    	have[u].clear();
    	for(const auto&p : g[u])
    		if(have[v].count(p.first)) reach[v].push_back(p.second);
    		else g[v].insert(p);
    	g[u].clear();
    }
    void run(int x){
    	sta[++top]=x,tag[x]=1;
    	while(top){
    		if(reach[x=sta[top]].empty()){tag[x]=2,--top;continue;}
    		int u=fd(reach[x].back());reach[x].pop_back();
    		if(!tag[u]){sta[++top]=u,tag[u]=1;continue;}
    		if(tag[u]==2) continue;
    		while(sta[top]!=u) mer(x,sta[--top]);
    		sta[top]=fd(x);
    	}
    }
    void ck(int u,int v,int col){
    	u=fd(u),v=fd(v);
    	if(u==v) return;
    	in[u]+=(have[u].count(col)?1:0);
    	in[v]+=(have[v].count(col)?1:0);
    }
    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(), 0);
    	n=r.size(),m=u.size();int i,mn=1e9;
    	for(i=0;i<m;++i){
    		if(c[i]==r[u[i]]) reach[u[i]].push_back(v[i]);
    		else g[u[i]].insert(make_pair(c[i],v[i]));
    		if(c[i]==r[v[i]]) reach[v[i]].push_back(u[i]);
    		else g[v[i]].insert(make_pair(c[i],u[i]));
    	}
    	for(i=0;i<n;++i) fa[i]=i,sz[i]=1,have[i].insert(r[i]);
    	for(i=0;i<n;++i) if(!tag[i]) run(i);
    	for(i=0;i<m;++i) ck(u[i],v[i],c[i]);
    	for(i=0;i<n;++i) if(!in[fd(i)]) ckMin(mn,sz[fd(i)]);
    	for(i=0;i<n;++i) if(!in[fd(i)]) ans[i]=(sz[fd(i)]==mn);
    	return ans;
    }
    
    
  • 相关阅读:
    (续)在深度计算框架MindSpore中如何对不持续的计算进行处理——对数据集进行一定epoch数量的训练后,进行其他工作处理,再返回来接着进行一定epoch数量的训练——单步计算
    YAML文件简介
    训练集验证集测试集的概念
    泛化误差
    drawio的打开方法
    移动硬盘无法被电脑识别
    r5 3600相当于英特尔什么级别
    Ubuntu WPS字体缺失配置
    pytorch深度学习cpu占用太高
    常用的架构设计原则-云原生架构设计快速入门
  • 原文地址:https://www.cnblogs.com/Kylin-xy/p/ioi2021-keys.html
Copyright © 2020-2023  润新知