tarjan & 2-set

缩点模板

inline void add(int u,int v)
{
	to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}

void tar(int u)
{
	low[u]=dfn[u]=++sgn;
	st[++top]=u;
	for(int e=he[u];e;e=nxt[e])
	{
		int v=to[e];
		if(!dfn[v]) tar(v),low[u]=min(low[u],low[v]);
			else if(!co[v]) low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u])
	{
		co[u]=++col;
		while(top&&st[top]!=u)
			co[st[top--]]=col;
		top--;
	}
}

时间复杂度\(O(m)\)

缩点后的染色是反DFN序,feel一下

例题1

Luogu P2272 [ZJOI2007]最大半连通子图

缩点后必须是条链,DP即可

其中涉及到计数,因为选的是节点,需要用set维护新建的边,防止重复

set<int>S[N];
void dfs(int u) {
	if(vis[u]) return;
	f[u]=a[u],g[u]=1;
	for(auto v:S[u]) {
		dfs(v);
		if(f[u]<f[v]+a[u]) {
			f[u]=f[v]+a[u];
			g[u]=g[v];
		} else if(f[u]==f[v]+a[u]) {
			g[u]=mo(g[u]+g[v]);
		}
	}
}

int main(){
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=m;i++) {
		int u,v; scanf("%d%d",&u,&v);
		V[u].push_back(v);
		E1[i]=u,E2[i]=v;
	}
	for(int i=1;i<=n;i++) {
		if(!dfn[i]) tar(i);	
	}
	for(int i=1;i<=m;i++) {
		int u=E1[i],v=E2[i];
		if(co[u]!=co[v]) {
			S[co[u]].insert(co[v]);
		}
	}
	int ans=0,sum=0;
	for(int i=1;i<=col;i++) {
		f[i]=a[i],g[i]=1;
		for(auto v:S[i]) {
			if(f[i]<f[v]+a[i]) {
				f[i]=f[v]+a[i];
				g[i]=g[v];
			} else if(f[i]==f[v]+a[i]) {
				g[i]=mo(g[i]+g[v]);
			}
		}
		if(ans==f[i]) sum=mo(sum+g[i]);
			else if(ans<f[i]){
				ans=f[i],sum=g[i]; 
			}
	}
	printf("%d\n%d\n",ans,sum);
	return 0;
}

2-set分析

将一个点裂成2个点,分别表示两种对立的状态,

边表示逻辑关系,如果\(i\)->\(j\),则\((i,j)\)连边

然后tarjan缩成DAG

显然如果两种对立的状态在一个SCC中,则无解

反之有解,可以通过按染色的顺序去找,每次选择dfn较大的,也就是co较小的(保证不冲突)

其中如果有些状态无解,可以将其连向有解的状态(如果有解的状态能推出无解的状态,就会形成环)

Vector 清空:

V[i].clear();
vector<int>().swap(V[i]);

例题1

Luogu P3209 [HNOI2010] 平面图判定

画张图,每条边可以在里面也可以在外面,这是两种对立的状态

如果两条边有重复,则必须一条在里一条在外

又因为平面图存在性质:

\[3n-6\leq m \]

考虑数学归纳法:\(n=3\)时,\(m=3\)

n>3时,每加一个点最多多3条边(和头尾形成三角形)

即证

int main() {
int T; scanf("%d",&T);
while(T--) {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&E1[i],&E2[i]);
	}
	for(int i=1;i<=n;i++) {
		int t; scanf("%d",&t);
		a[t]=i;
	}
	if(m>3*n-6) {
		puts("NO"); continue;
	}
	for(int i=1;i<=m+m;i++) {
		V[i].clear(); vector<int>().swap(V[i]);
	}
	for(int i=1;i<=m;i++) {
		int u=a[E1[i]],v=a[E2[i]];
		memset(vis,0,sizeof(vis));
		if(u<v) {
			for(int j=u+1;j<v;j++) vis[j]=1;
			vis[u]=vis[v]=-1;
		} else {
			for(int j=1;j<v;j++) vis[j]=1;
			for(int j=u+1;j<=n;j++) vis[j]=1;
			vis[u]=vis[v]=-1;
		}
		for(int j=i+1;j<=m;j++) {
			u=a[E1[j]],v=a[E2[j]];
			if(vis[u]!=-1&&vis[v]!=-1&&vis[u]^vis[v]) {
				V[i].push_back(j+m);
				V[j+m].push_back(i);
			}
		}
	}
	memset(dfn,0,sizeof(dfn));
	memset(co,0,sizeof(co));
	tot=col=top=0;
	for(int i=1;i<=m+m;i++) {
		if(!dfn[i]) tar(i);
	}
	bool fl=0;
	for(int i=1;i<=m;i++) {
		if(co[i]==co[i+m]) {
			puts("NO"); fl=1; break;
		}
	}
	if(!fl) puts("YES");
}
return 0;
}

例题2

ybtoj 建农场

二分答案转化为判定问题,2-set

struct Po{int x,y; }a[N],S,T,E1[N],E2[N];
inline int D(Po x,Po y) {
	return abs(x.x-y.x)+abs(x.y-y.y);
}
bool check(int mid) {
	for(int i=1;i<=n+n;i++) {
		V[i].clear(); vector<int>().swap(V[i]);
	}
	for(int i=1;i<=A;i++) {
		int u=E1[i].x,v=E1[i].y;
		V[u+n].push_back(v),V[u].push_back(v+n);
		V[v+n].push_back(u),V[v].push_back(u+n);
	}
	for(int i=1;i<=B;i++) {
		int u=E2[i].x,v=E2[i].y;
		V[u].push_back(v),V[u+n].push_back(v+n);
		V[v+n].push_back(u+n),V[v].push_back(u);
	}
	for(int i=1;i<=n;i++) {
		for(int j=1;j<i;j++) {
			if(D(a[i],S)+D(a[j],T)+D(S,T)>mid) {
				V[j+n].push_back(i+n);
				V[i].push_back(j);
			}
			if(D(a[i],T)+D(a[j],S)+D(S,T)>mid) {
				V[i+n].push_back(j+n);
				V[j].push_back(i);
			}
			if(D(a[i],S)+D(a[j],S)>mid) {
				V[i].push_back(j+n);
				V[j].push_back(i+n);
			}
			if(D(a[i],T)+D(a[j],T)>mid) {
				V[i+n].push_back(j);
				V[j+n].push_back(i);
			}
		}
	}
	memset(dfn,0,sizeof(dfn));
	memset(co,0,sizeof(co));
	tot=col=top=0;
	for(int i=1;i<=n+n;i++) {
		if(!dfn[i]) tar(i);
	}
	for(int i=1;i<=n+n;i++) {
		if(co[i]==co[i+n]) return 0;
	}
	return 1;
}
int main() {
while(scanf("%d%d%d",&n,&A,&B)!=EOF) {
	scanf("%d%d%d%d",&S.x,&S.y,&T.x,&T.y);
	for(int i=1;i<=n;i++) {
		scanf("%d%d",&a[i].x,&a[i].y);
	}
	for(int i=1;i<=n+n;i++) {
		V[i].clear(); vector<int>().swap(V[i]);
	}
	for(int i=1;i<=A;i++) {
		int u,v; scanf("%d%d",&u,&v);
		V[u+n].push_back(v),V[u].push_back(v+n);
		V[v+n].push_back(u),V[v].push_back(u+n);
		E1[i]=(Po){u,v};
	}
	for(int i=1;i<=B;i++) {
		int u,v; scanf("%d%d",&u,&v);
		V[u].push_back(v),V[u+n].push_back(v+n);
		V[v+n].push_back(u+n),V[v].push_back(u);
		E2[i]=(Po){u,v};
	}
	memset(dfn,0,sizeof(dfn));
	memset(co,0,sizeof(co));
	tot=col=top=0;
	for(int i=1;i<=n+n;i++) {
		if(!dfn[i]) tar(i);
	}
	bool fl=0;
	for(int i=1;i<=n;i++) {
		if(co[i]==co[i+n]) {
			puts("-1"); fl=1; break;
		}
	}
	if(fl) continue;
	int l=0,r=1e8,mid,ans=0;
	while(l<=r) {
		mid=l+r>>1;
		if(check(mid)) ans=mid,r=mid-1;
			else l=mid+1;
	}
	printf("%d\n",ans);
}
return 0;
}

例题3

Luogu P3825 [NOI2017] 游戏

如果没有\(x\),每次只有两种状态2-set

如果有\(x\),可以枚举\(x\)的3种状态,但观察可以发现,只需要图\(a\)和图\(b\)带入\(x\)判断是否可行即包含了3种状态

注意:如果\((u,h_u,v,h_v)\)\(v\)\(h_v\)的状态不存在,\(u\)\(h_u\)的状态也不存在

int main() {
	scanf("%d%d%s%d",&n,&K,s+1,&m);
	for(int i=1;i<=m;i++) {
		char ch=getchar();
		int u=0,v=0,k1=0,k2=0;
		while(ch<'0'||ch>'9') ch=getchar();
		while(ch>='0'&&ch<='9') {
			u=(u<<1)+(u<<3)+ch-'0';
			ch=getchar();
		}
		while(ch<'A'||ch>'C') ch=getchar();
		k1=ch-'A';
		while(ch<'0'||ch>'9') ch=getchar();
		while(ch>='0'&&ch<='9') {
			v=(v<<1)+(v<<3)+ch-'0';
			ch=getchar();
		}
		while(ch<'A'||ch>'C') ch=getchar();
		k2=ch-'A';
		E1[i]=(A){u,k1},E2[i]=(A){v,k2};
	}
	for(int i=1,j=1;i<=n&&j<=K;i++) {
		if(s[i]=='x') loc[j++]=i;
	}
	for(int i=0;i<(1<<K);i++) {
		for(int j=1;j<=K;j++) {
			if(i&(1<<j-1)) s[loc[j]]='a';
				else s[loc[j]]='b';
		}
		for(int j=1;j<=n+n;j++) {
			V[j].clear();
			vector<int>().swap(V[j]);
		}
		for(int j=1;j<=m;j++) {
			int u=E1[j].u,k1=E1[j].v,v=E2[j].u,k2=E2[j].v;
			if(s[u]-'a'==k1) continue;
			if(s[v]-'a'==k2) {
				if(s[u]=='a') k1--;
				if(s[u]=='b') {
					if(k1) k1--;
				}
				V[u+k1*n].pb(u+(!k1)*n);
				continue;
			}
			if(s[u]=='a') k1--;
			if(s[v]=='a') k2--;
			if(s[u]=='b') {
				if(k1) k1--;
			}
			if(s[v]=='b') {
				if(k2) k2--;
			}
			V[u+k1*n].pb(v+k2*n);
			V[v+n*(!k2)].pb(u+n*(!k1));
		}
		memset(dfn,0,sizeof(dfn));
		memset(co,0,sizeof(co));
		tot=top=col=0;
		for(int j=1;j<=n+n;j++) {
			if(!dfn[j]) tar(j);
		}
		bool fl=0;
		for(int j=1;j<=n;j++) {
			if(co[j]==co[j+n]) {
				fl=1; break;
			}
		}
		if(!fl) {
			for(int j=1;j<=n;j++) {
				if(co[j]<co[j+n]) {
					if(s[j]=='a') putchar('B');
						else putchar('A');
				} else {
					if(s[j]=='c') putchar('B');
						else putchar('C'); 
				}
			}
			return 0;
		}
	}
	puts("-1");
	return 0;
}
posted @ 2021-03-15 09:48  wwwsfff  阅读(44)  评论(0编辑  收藏  举报