題解 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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM