LOJ#3524. 「IOI2021」钥匙

20 篇文章 0 订阅
20 篇文章 0 订阅

Description

Solution

  • 首先注意到如果 x − > y , y − > z x->y,y->z x>y,y>z,那么 x − > z x->z x>z
  • 因此考虑 x x x能够到 y y y,如果 y y y不能够到 x x x,那么 p x > p y p_x>p_y px>py x x x就失去了意义,同理所有能够到达 x x x的点也失去了意义。
  • 因此可以考虑维护一个栈,每一个栈中的元素是一个强连通分量,并且它们形成了一条链。每一次从栈顶进行扩展,如果能够到一个无意义的点,那么栈中所有元素都无意义。如果到了一个栈中的点,就缩一下强连通分量。如果到了一个未到的点,就考虑这个点能否回来,因此将其将入栈中即可。最终在有意义的强连通分量中找即可。
  • 合并需要用线段树维护一个钥匙集合,叶子挂一个链表表示连的边,外面用并查集就可以做到 ( n + m ) l o g   n (n+m)log\ n (n+m)log n.
#include "keys.h"
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 300005
#define maxm 12000005
using namespace std;

int n,i,j,k,c[maxn];
int em,e[maxn*2],nx[maxn*2];
int tot,t[maxm],tc[maxm];
int ls[maxm],tl[maxm],tr[maxm],rt[maxn],sz[maxm];

void change(int &x,int l,int r,int p){
	if (!x) x=++tot;  
	if (l==r) {tc[x]=1,t[x]=tc[x]&(ls[x]>0); return;}
	int mid=(l+r)>>1;
	if (p<=mid) change(tl[x],l,mid,p); else change(tr[x],mid+1,r,p);
	t[x]=t[tl[x]]|t[tr[x]];
}

void add(int &x,int l,int r,int p,int y){
	if (!x) x=++tot;
	if (l==r) {em++,e[em]=y,nx[em]=ls[x],ls[x]=em,sz[x]++;return;}
	int mid=(l+r)>>1;
	if (p<=mid) add(tl[x],l,mid,p,y); else add(tr[x],mid+1,r,p,y);
}

void insert(int x,int y,int z){add(rt[x],1,n,z,y),add(rt[y],1,n,z,x);}

int fa[maxn],g[maxn],vis[maxn],in[maxn],d[maxn],bz[maxn];
int father(int x){return (fa[x]==x)?x:fa[x]=father(fa[x]);}
void merge(int x,int y,int l,int r){
	if (l==r){
		if (sz[x]<sz[y]) swap(ls[x],ls[y]),swap(sz[x],sz[y]);
		sz[x]+=sz[y];tc[x]|=tc[y],t[x]=tc[x]&(sz[x]>0);
		for(int i=ls[y];i;){int k=nx[i];nx[i]=ls[x],ls[x]=i;i=k;}
		return;
	}
	int mid=(l+r)>>1;
	if (!tl[x]) tl[x]=tl[y]; else 
	if (tl[y]) merge(tl[x],tl[y],l,mid);
	if (!tr[x]) tr[x]=tr[y]; else 
	if (tr[y]) merge(tr[x],tr[y],mid+1,r);
	t[x]=t[tl[x]]|t[tr[x]];
}

int find(int x,int l,int r){
	if (!t[x]) return 0;
	if (l==r) {
		int k=ls[x]; 
		sz[x]--,ls[x]=nx[k],t[x]=tc[x]&(ls[x]>0); return e[k];
	}
	int mid=(l+r)>>1,k;
	if (t[tl[x]]) k=find(tl[x],l,mid); else k=find(tr[x],mid+1,r);
	t[x]=t[tl[x]]|t[tr[x]]; return k;
}

int Findone(int x){
	int y=find(rt[x],1,n);
	while (y&&father(y)==father(x))
		y=find(rt[x],1,n);
	return y;
}

void Merge(int x,int y){
	g[y]+=g[x],fa[x]=y;
	if (!rt[y]) rt[y]=rt[x]; 
	else if (rt[x]) merge(rt[y],rt[x],1,n);
}

vector<int> find_reachable(vector<int> r,vector<int> u,vector<int> v,vector<int> w) {
	n=r.size();	
	for(i=1;i<=n;i++) c[i]=r[i-1]+1;
	for(i=0;i<u.size();i++) insert(u[i]+1,v[i]+1,w[i]+1);
	for(i=1;i<=n;i++) 
		change(rt[i],1,n,c[i]);
	for(i=1;i<=n;i++) fa[i]=i,g[i]=1;
	for(int S=1;S<=n;S++) if (!vis[S]){
		d[d[0]=1]=S,in[S]=vis[S]=1; int tp=0;
		while (1){
			int x=father(d[d[0]]),y=father(Findone(x));
			if (!y) break;
			if (vis[y]){
				if (in[y]){
					while (d[d[0]]!=y) {
						Merge(d[d[0]],d[d[0]-1]);
						in[d[d[0]]]=0,d[0]--;
					}	
				} else {tp=1;break;}
			} else d[++d[0]]=y,vis[y]=in[y]=1;
		}
		for(i=1;i<=d[0]-1+tp;i++) bz[d[i]]=1;
		while (d[0]) in[d[d[0]--]]=0;
	}
	int mi=n;
	for(i=1;i<=n;i++) if (father(i)==i&&!bz[i]) mi=min(mi,g[i]);
	vector<int> ans;
	for(i=1;i<=n;i++) ans.push_back(g[father(i)]==mi&&!bz[father(i)]);
	return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值