ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题解 竞赛图

2021-08-15 06:33:09  阅读:215  来源: 互联网

标签:出边 竞赛 min 题解 reach low lowbit define


传送门

考试的时候想到了一个应该有60pts的 \(O(2^nn^2T)\) 但是 \(n^2\) 只是枚举边的状压
然后挂成40pts,从中午12点拍到晚上5点半没拍出来
记得哪天去要下这题数据
考试的时候如何发现这些奇奇怪怪的性质啊……

正解需要 \(O(2^nT)\) 才能过
所以枚举点集跑check一定会炸,考虑可行性DP
尤其注意本题的图两点之间一定有边,且一定只指向一个方向
发现对于一个强联通的诱导子图S,必然存在且仅存在一个强连通的子图T,满足T内的点到S-T中所有点的边都是从T到S-T的
那么发现这个玩意有可扩展性
对于一个强连通分量,令其出边集合为R,那么S与R的子集的并一定不合法
那么问题转化为 \(O(2^n)\) 预处理出所有出边集合
想了半天没想到,但其实很简单

\[reach_i = reach_{lowbit(i)}\ \&\ reach_{i\oplus lowbit(i)} \]

注意要特判一下 \(i=lowbit(i)\) ,也即 \(i\) 中只有一位的情况,或直接将 \(reach_0\) 置为全1

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 25
#define ll long long 
#define reg register int
#define min(a, b) ((a)<(b)?(a):(b))
//#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;
bool mp[N][N];
int head[N], size;
struct edge{int to, next;}e[N*N*2];
inline void add(int s, int t) {e[++size].to=t; e[size].next=head[s]; head[s]=size;}

namespace force{
	int vis, s;
	void dfs(int u) {
		vis|=1<<u;
		for (reg i=0; i<n; ++i) 
			if (mp[u][i] && s&(1<<i) && !(vis&(1<<i)))
				dfs(i);
	}
	void solve() {
		int lim=1<<n, ans=0;
		for (s=0; s<lim; ++s) {
			for (reg i=0; i<n; ++i) if (s&(1<<i)) {
				vis=0; dfs(i);
				if (vis!=s) goto jump;
			}
			++ans;
			jump: ;
		}
		printf("%d\n", ans);
	}
}

namespace task1{
	int vis, s, tot;
	int dfn[N], low[N];
	bool tarjan(int u) {
		//if (s==(1<<n)-1) cout<<"tarjan "<<u<<endl;
		vis|=1<<u;
		dfn[u]=low[u]=++tot;
		bool flag=0;
		//for (reg i=0; i<n; ++i) 
		//	if (mp[u][i] && s&(1<<i)) {
		for (int t=head[u],i; ~t; t=e[t].next) {
			i = e[t].to;
			if (!(s&(1<<i))) continue;
				flag=1;
				//cout<<"visit: "<<i<<endl;
				if (!(vis&(1<<i))) {
					if (!tarjan(i)) return 0;
					if (low[i]>dfn[u]) return 0;
					low[u]=min(low[u], low[i]);
				}
				else low[u]=min(low[u], low[i]); //, cout<<dfn[i]<<endl;
			}
		//if (s==(1<<n)-1) cout<<"tarjan "<<u<<" return "<<flag<<' '<<low[u]<<endl;
		return flag;
	}
	void solve() {
		int lim=1<<n, ans=1;
		for (s=1; s<lim; ++s) {
			//memset(dfn, 0, sizeof(int)*n);
			//memset(low, 0, sizeof(int)*n);
			vis=tot=0;
			for (reg i=0; i<n; ++i) if (s&(1<<i)) {
				//if (tarjan(i)) cout<<bitset<3>(s)<<' '<<bitset<3>(vis)<<' '<<i<<endl;
				if (tarjan(i) && vis==s) {
					++ans;
					//cout<<bitset<5>(s)<<endl;
				}
				break;
			}
		}
		printf("%d\n", ans+n);
	}
}

namespace task{
	int to[1<<24];
	bool vis[1<<24];
	void solve() {
		memset(to, 0, sizeof(to));
		memset(vis, 0, sizeof(vis));
		int lim=1<<n, ans=1<<n;
		for (reg i=0; i<n; ++i) 
			for (reg j=0; j<n; ++j)
				if (mp[i][j])
					to[1<<i]|=1<<j;
		for (reg s=1; s<lim; ++s) {
			if (s!=(s&-s)) to[s]=to[s^(s&-s)]&to[s&-s];
			if (vis[s]) {--ans; continue;}
			for (reg i=to[s]; i; i=(i-1)&to[s]) 
				vis[s|i]=1;
		}
		printf("%d\n", ans);
	}
}

signed main()
{
	int T;
	
	T=read();
	while (T--) {
		//memset(mp, 0, sizeof(mp));
		memset(head, -1, sizeof(head));
		n=read();
		for (reg i=0; i<n; ++i) 
			for (reg j=0; j<n; ++j) {
				mp[i][j]=read();
				//if (read()) add(i, j);
			}
		//force::solve();
		//task1::solve();
		task::solve();
	}
	
	return 0;
}

标签:出边,竞赛,min,题解,reach,low,lowbit,define
来源: https://www.cnblogs.com/narration/p/15142542.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有