题解 CF1338E

Posted whx1003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 CF1338E相关的知识,希望对你有一定的参考价值。

题目链接

编了一个做法发现没人提出过,所以记录一下(

直接考虑图强连通的情况。发现连向一个点的所有点一定构成一条链。于是考虑求出每个点到一号点的距离。

考虑构造一棵 BFS 树。定义两个点 \\(x,y\\) 的小于关系,\\(x<y\\) 当且仅当:

  • 如果 \\(dis_x\\neq dis_y\\)\\(dis_x<dis_y\\)
  • 否则如果 \\(fa_x\\neq fa_y\\)\\(fa_x<fa_y\\)
  • 否则存在 \\(y\\to x\\) 的边

然后每个点的父亲为 \\(dis\\) 恰好等于它减一的,它连向的,所有点中的最小的点。大约是如下的一棵树:

将这张图按照给出的小于关系重标号之后,有一些非常好的性质:

  • \\(mx=fa_n\\),于是对于所有 \\(i\\in[2,mx-1]\\),都有 \\(fa_i=1\\)
  • 两个点 \\(j,i\\),如果 \\(fa_j\\leq i\\) 那么存在 \\(j\\to i\\) 的边,否则存在 \\(i\\to j\\) 的边

一个直接的推论是这棵树的层数不会超过 \\(4\\),且所有第 \\(4\\) 层的节点的父亲都必须是第 \\(3\\) 层的第一个节点。

于是不同的 \\(dis\\) 关系只会有 \\(6\\) 中,可以直接借此来讨论,复杂度 \\(\\mathcal O(n^2)\\)

#include<queue>
#include<cstdio>
#include<algorithm>

const int maxn = 8005;

int val[200];

int n, deg[maxn], tot, buc[maxn];
bool g[maxn][maxn], G[maxn][maxn], vis[maxn];
int dis[maxn], p[maxn], rk[maxn], fa[maxn], son[maxn];

bool chk[maxn][maxn], S[maxn][maxn];
inline bool sml(int x, int y) {
	if(x == y) return false;
	if(chk[x][y]) return S[x][y];
	
	chk[x][y] = true;
	if(dis[x] != dis[y]) return S[x][y] = dis[x] < dis[y];
	if(fa[x] != fa[y]) return S[x][y] = sml(fa[x], fa[y]);
	return S[x][y] = G[y][x];
}

std::vector<int> d[4];

int main() {
	val[\'0\'] = 0, val[\'1\'] = 1, val[\'2\'] = 2, val[\'3\'] = 3;
	val[\'4\'] = 4, val[\'5\'] = 5, val[\'6\'] = 6, val[\'7\'] = 7;
	val[\'8\'] = 8, val[\'9\'] = 9, val[\'A\'] = 10, val[\'B\'] = 11;
	val[\'C\'] = 12, val[\'D\'] = 13, val[\'E\'] = 14, val[\'F\'] = 15;
	
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) {
		getchar();
		for(int j = 1; j * 4 <= n; ++j) {
			int v = val[getchar()];
			g[i][j * 4 - 3] = v >> 3 & 1;
			g[i][j * 4 - 2] = v >> 2 & 1;
			g[i][j * 4 - 1] = v >> 1 & 1;
			g[i][j * 4 - 0] = v >> 0 & 1;
		}
	}
	
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			deg[j] += g[i][j];
	
	std::queue<int> q;
	for(int i = 1; i <= n; ++i)
		if(deg[i] == 0) q.push(i);
	
	int cnt = n; long long ans = 0;
	while(!q.empty()) {
		int u = q.front(); vis[u] = 1, q.pop();
		
		--cnt, ans += cnt * (1 + 614ll * n);
		for(int i = 1; i <= n; ++i)
			if(g[u][i] && !--deg[i]) q.push(i);
	}
	
	for(int i = 1; i <= n; ++i)
		if(!vis[i]) buc[++tot] = i;
	
	for(int i = 1; i <= tot; ++i) {
		for(int j = 1; j <= tot; ++j)
			G[i][j] = g[buc[i]][buc[j]];
		G[i][i] = 1;
	}
	n = tot;
	
	std::fill(dis, dis + n + 1, 1E+9);
	
	q.push(1), dis[1] = 0;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		for(int i = 1; i <= n; ++i)
			if(G[i][u] && dis[i] > dis[u] + 1)
				dis[i] = dis[u] + 1, q.push(i);
	}
	
	for(int i = 1; i <= n; ++i) d[dis[i]].push_back(i);
	for(int i = 1; i <= 3; ++i)
		for(int x : d[i]) {
			for(int y : d[i - 1])
				if(G[x][y] && (!fa[x] || sml(y, fa[x]))) fa[x] = y;
			++son[fa[x]];
		}
	
	for(int i = 1; i <= n; ++i) p[i] = i;
	std::sort(p + 1, p + n + 1, sml);
	for(int i = 1; i <= n; ++i) rk[p[i]] = i;
	
	for(int i = 1; i <= n; ++i) son[p[i]] += son[p[i - 1]];
	
	for(int i = 2; i <= n; ++i)
		ans += dis[i] + 1 + (dis[i] == 1) + (son[i] == son[1]);
	
	for(int i = 2; i <= n; ++i) {
		for(int j = i + 1; j <= n; ++j) {
			int x = i, y = j;
			
			if(!sml(x, y)) std::swap(x, y);
			if(dis[x] == 1 && dis[y] == 1) ans += 3 + (son[y] == son[x]);
			if(dis[x] == 1 && dis[y] == 2) ans += 3;
			if(dis[x] == 2 && dis[y] == 2) ans += 3 + (fa[x] == fa[y]);
			if(dis[x] == 1 && dis[y] == 3) ans += 3 + sml(x, fa[fa[y]]);
			if(dis[x] == 2 && dis[y] == 3) ans += 3 + sml(x, fa[y]);
			if(dis[x] == 3 && dis[y] == 3) ans += 4;
		}
	}
	
	printf("%lld\\n", ans);
}

以上是关于题解 CF1338E的主要内容,如果未能解决你的问题,请参考以下文章

题解 CF1063B Labyrinth

CF Round #631 题解

CF886E 题解

CF524B 题解

题解 CF1216D Swords

题解 CF1354D Multiset