题解 主仆见证了 Hobo 的离别

传送门

题面里说每个元件只会被融合一次,但想着想着题就忘了这个条件了……

首先题意可以转化到图上,即(以只考虑并为例)要求 \(x\)\(y\) 的祖先或 \(x, y\) 之间由 \(k=1\) 的边相连
然后觉得是个有向图,判祖先得离线了用bitset挺麻烦的就跑路了
然而原图实际上是棵树……

  • 树上判断两个点是否有祖孙关系:可以用dfs序是否存在包含关系判断
  • 树上判断有祖先关系的点是否恰好由某一类边相连:可以对每一类边分别建立并查集,记录当前集合中深度最浅的点,判断与较浅的点的深度就可以了
    求出lca的话也可以拓展到树上任意两点
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define ll long long
#define pb push_back
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
bitset<10010> s[5010];

namespace force{
	int tot;
	bool all_one=1;
	void solve() {
		// cout<<double(sizeof(s))/1024/1024<<endl;
		tot=n;
		for (int i=1; i<=n; ++i) s[i][i]=1;
		for (int i=1,op,k,x,y; i<=m; ++i) {
			if (read()&1) {
				x=read(); y=read();
				if (all_one || n>2010) puts((x&y)==x?"1":"0");
				else {
					if (x<=y) puts((x&y)==x?"1":"0");
					else printf("%d\n", rand()&1);
				}
			}
			else {
				op=read(); k=read(); ++tot;
				if (k!=1) all_one=0;
				if (op&1) {
					for (int j=1; j<=k; ++j) s[tot]|=s[read()];
					// s[tot][tot]=1;
				}
				else {
					s[tot]=s[read()];
					for (int j=1; j<k; ++j) s[tot]&=s[read()];
					// s[tot][tot]=1;
				}
			}
		}
		// cout<<s[5]<<endl;
		exit(0);
	}
}

namespace task{
	int head[N], size, dsua[N], dsuo[N], dep[N], cnt[N], in[N], out[N], mdepa[N], mdepo[N], now, tot;
	vector<int> v[N];
	int tp[N], op[N], x[N], y[N];
	struct edge{int to, next;}e[N];
	inline void add(int s, int t) {e[++size].to=t; e[size].next=head[s]; head[s]=size;}
	inline int finda(int p) {return dsua[p]==p?p:dsua[p]=finda(dsua[p]);}
	inline int findo(int p) {return dsuo[p]==p?p:dsuo[p]=findo(dsuo[p]);}
	inline void unia(int a, int b) {a=finda(a); b=finda(b); mdepa[b]=min(mdepa[b], mdepa[a]); dsua[a]=b;}
	inline void unio(int a, int b) {a=findo(a); b=findo(b); mdepo[b]=min(mdepo[b], mdepo[a]); dsuo[a]=b;}
	void dfs(int u) {
		// cout<<"dfs: "<<u<<endl;
		in[u]=++now;
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			dep[v]=dep[u]+1;
			dfs(v);
		}
		out[u]=++now;
	}
	void solve() {
		tot=n;
		memset(head, -1, sizeof(head));
		for (int i=1,k; i<=m; ++i) {
			tp[i]=read();
			if (tp[i]&1) x[i]=read(), y[i]=read();
			else {
				op[i]=read(); k=read(); ++tot;
				for (int j=1,t; j<=k; ++j) {
					v[i].pb(t=read());
					add(tot, t); ++cnt[t];
				}
			}
		}
		++tot;
		// cout<<"tot: "<<tot<<endl;
		for (int i=1; i<tot; ++i) if (!cnt[i]) add(tot, i);
		dep[tot]=1; dfs(tot); now=n;
		// cout<<"dep: "; for (int i=1; i<=tot; ++i) cout<<dep[i]<<' '; cout<<endl;
		for (int i=1; i<=tot; ++i) dsua[i]=dsuo[i]=i, mdepa[i]=mdepo[i]=dep[i];
		for (int i=1; i<=m; ++i) {
			if (tp[i]&1) {
				// cout<<"xy: "<<x[i]<<' '<<y[i]<<' '<<in[x[i]]<<' '<<out[x[i]]<<' '<<in[y[i]]<<' '<<out[y[i]]<<endl;
				// cout<<"mdep: "<<mdepo[findo(x[i])]<<endl;
				if (x[i]==y[i]) puts("1");
				else if (in[x[i]]<in[y[i]] && out[y[i]]<out[x[i]]) {
					puts(mdepa[finda(y[i])]<=dep[x[i]]?"1":"0");
				}
				else if (in[y[i]]<in[x[i]] && out[x[i]]<out[y[i]]) {
					puts(mdepo[findo(x[i])]<=dep[y[i]]?"1":"0");
				}
				else puts("0");
			}
			else {
				++now;
				if (v[i].size()==1) unia(now, v[i][0]), unio(now, v[i][0]);
				else {
					if (op[i]&1) for (auto j:v[i]) unio(now, j);
					else for (auto j:v[i]) unia(now, j);
				}
			}
		}
	}
}

signed main()
{
	freopen("friendship.in", "r", stdin);
	freopen("friendship.out", "w", stdout);
	random_device(seed);
	srand(seed());

	n=read(); m=read();
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2021-10-16 07:07  Administrator-09  阅读(0)  评论(0编辑  收藏  举报