【BZOJ5033】【JSOI2014】Strong connectivity graph

【Title link】

【Key points of thinking】

  • The first question is essentially the size of the largest strongly connected component in the graph, and Tarjan's algorithm is sufficient.
  • For the second question, let's shrink the figure first. Obviously the answer has a lower bound\(max\{cntin,cntout\}\), where \(cntin\) is the number of points with in-degree 0, \(cntout\ ) is the number of points whose out-degree is 0.
  • Both examples in the question satisfy that the answer is exactly lower bound, so we guess that the answer is always lower bound and try to construct a set of solutions.
  • We arrange all points with in-degree 0 on one side, and points with out-degree 0 on the other side (if a point has both in-degree and out-degree 0, we will arrange it on both sides), if an in-degree is The point \(A\) of 0 can reach a point \(B\) with zero out-degree, and connect the edge \(A\Rightarrow B\), which will form a bipartite graph.
  • Obviously, if we connect several edges to make the bipartite graph strongly connected, then there must be a solution to connect the original graph.
  • Since the answer is guaranteed to be within 1000, according to the lower bound of the answer, the number of points on both sides of this bipartite graph must be within 1000.
  • This bipartite graph satisfies the property: the degree of each point is non-zero.
  • We find a maximal match of this bipartite graph (not necessarily the maximal, just maximal), and connect the \(x\) point pairs on the match in order to form a ring, which will require increasing \(x\) edges.
  • Consider the remaining points that are not connected to the ring, since this is a great match of the bipartite graph, and the degree of each point on the bipartite graph is non-zero, and the in-degree of each point not on the ring is 0 The point must be able to reach the ring, and every point with out-degree 0 that is not on the ring must be reachable from the ring, otherwise we can obviously add an edge to construct a larger match.
  • Therefore, every time we find a pair of points \(x\), \(y\) that are not on the ring with in/out degree 0, and connect the edges \(y\Rightarrow x\), we can make \(x\ ), \(y\) are connected with the ring. There may also be one of the points left that are not on the ring with in/out degree 0, just connect each point to the ring with an edge.
  • So far, we have shared \(max\{cntin,cntout\}\) edges.
  • BZOJ does not have SPJ and cannot evaluate this question correctly. The following code has been tested by the author himself.
  • Time complexity\(O(\frac{NM}{w}+Ans^2)\) (where \(w=64\)).
  • The SPJ of this question is also attached. Before running, please ensure that there are three files " scg.in ", " scg.out " and " scg.ans " in the same directory.

[with the SPJ of this question]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string>
#include<algorithm>
#include<fstream>
#include<sstream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<deque>
#include<cassert>
#include<complex>
using namespace std;

const int MaxN = 10000 + 10;

const char* inFile = "scg.in";
const char* outFile = "scg.out";
const char* ansFile = "scg.ans";
const char* logFile = "check.txt";

int grade(int s = 0, string msg = string("Wrong Answer!")) {
	FILE * file = fopen(logFile, "w");
	if(s == 0) {
		fprintf(file, "%s\n0\n", msg.c_str());
		return 0;
	}
	fprintf(file, "FC:\n%d\n%s\n",s,msg.c_str());
	return 0;
}

vector<int> adj[MaxN], rev[MaxN];
int N, M;
bool vis[MaxN];
int Q[MaxN], n;
void dfs1(int u) {
	vis [u] = true;
	for(int i=0;i<adj[u].size();++i)
		if(!vis[adj[u][i]])
			dfs1(adj[u][i]);
	Q[n ++] = u;
}
void dfs2(int u) {
	vis [u] = true;
	for(int i=0;i<rev[u].size();++i)
		if(!vis[rev[u][i]])
			dfs2(rev[u][i]);
}
bool check() {
	fill (vis, vis + 1 + N, 0);
	for(int i=1;i<=N;++i)
		if(!vis[i])
			dfs1 (i);
	fill (vis, vis + 1 + N, 0);
	dfs2(Q[n - 1]);
	for(int i=1;i<=N;++i)
		if(!vis[i]) return false;
	return true;
}

int A, C, user_A, user_C;

int loadInput(FILE* inf) {
	if(fscanf(inf, "%d %d", &N, &M) != 2 || N < 3 || N > 10000 || M < 0 || M > 200000)
		return 0;
	for(int i=0;i<M;++i) {
		int x, y;
		if(fscanf(inf, "%d %d", &x, &y) != 2 || min(x,y) < 1 || max(x,y)>N) {
			
			cerr << "error on line #"<<i+2<<" < "<<x<<" , "<<y<<">"<<endl;
			
			return 0;
		}
		adj[x].push_back(y);
		rev[y].push_back(x);
	}
	return 1;
}

int loadAnswer (FILE *inf) {
	if(fscanf(inf, "%d", &C) != 1 || C < 1 || C > N) return 0;
	if(fscanf(inf, "%d", &A) != 1 || A < 0 || A > N || A > 1000) return 0;
	return 1;
}


const int FullScore = 10;
const int LoScore = 2;
const int MidScore = 4;


void loadOutput(FILE* inf) {
	if(fscanf(inf, "%d", &user_C) != 1) {
		fclose(inf);
		exit(grade(0, "Format Error!"));
	}
	if(user_C != C) {
		fclose(inf);
		exit(grade(0, "Wrong Answer!"));
	}

	if(fscanf(inf, "%d", &user_A) != 1) {
		fclose(inf);
		exit(grade(LoScore, "<C> Correct but <A> not found! "));
	}
	if(user_A != A) {
		fclose(inf);
		exit(grade(LoScore, "<C> Correct but <A> Wrong!"));
	}
	
	int x, y;
	for(int i=0;i<A;++i) {
		if(fscanf(inf, "%d %d", &x, &y) != 2 || min(x, y) < 1 || max(x, y) > N) {
			fclose(inf);
			exit(grade(MidScore, "<C> and <A> Correct! Solution Format Error!"));
		}
		adj[x].push_back(y);
		rev[y].push_back(x);
	}
	fclose(inf);
	
	if(check())
		grade(10, "Correct!");
	else
		grade(MidScore, "<C> and <A> Correct but Wrong Solution!");
	
	exit(0);
}

int main() {
	grade(0, "Internal Error!");

	FILE *file = fopen(inFile, "r");
	if(file == NULL) exit(grade(0, "No Input File!"));
	if(!loadInput(file)) {
		fclose(file);
		exit(grade(0, "Input File Format Error!"));
	}
	fclose(file);
	
	file = fopen(ansFile, "r");
	if(file == NULL) exit(grade(0, "No Answer File!"));
	if(!loadAnswer(file)){
		fclose(file);
		exit(grade(0, "Answer File Format Error!"));
	}
	fclose(file);
	
	file = fopen(outFile, "r");
	if(file == NULL) exit(grade(0, "No Output File!"));
	loadOutput(file);
}

[Lemon version SPJ of this question]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string>
#include<algorithm>
#include<fstream>
#include<sstream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<deque>
#include<cassert>
#include<complex>
using namespace std;

const int MaxN = 10000 + 10;

char* inFile;
char* outFile;
char* ansFile;
char* logFile;
char * scoreFile;

int grade(int s = 0, string msg = string("Wrong Answer!")) {
	FILE * file = fopen(logFile, "w");
	FILE * score = fopen(scoreFile, "w");
	fprintf(score, "%d\n", s);
	if(s == 0) {
		fprintf(file, "%s\n0\n", msg.c_str());
		return 0;
	}
	fprintf(file, "FC:\n%d\n%s\n",s,msg.c_str());
	return 0;
}

vector<int> adj[MaxN], rev[MaxN];
int N, M;
bool vis[MaxN];
int Q[MaxN], n;
void dfs1(int u) {
	vis [u] = true;
	for(int i=0;i<adj[u].size();++i)
		if(!vis[adj[u][i]])
			dfs1(adj[u][i]);
	Q[n ++] = u;
}
void dfs2(int u) {
	vis [u] = true;
	for(int i=0;i<rev[u].size();++i)
		if(!vis[rev[u][i]])
			dfs2(rev[u][i]);
}
bool check() {
	fill (vis, vis + 1 + N, 0);
	for(int i=1;i<=N;++i)
		if(!vis[i])
			dfs1 (i);
	fill (vis, vis + 1 + N, 0);
	dfs2(Q[n - 1]);
	for(int i=1;i<=N;++i)
		if(!vis[i]) return false;
	return true;
}

int A, C, user_A, user_C;

int loadInput(FILE* inf) {
	if(fscanf(inf, "%d %d", &N, &M) != 2 || N < 3 || N > 10000 || M < 0 || M > 200000)
		return 0;
	for(int i=0;i<M;++i) {
		int x, y;
		if(fscanf(inf, "%d %d", &x, &y) != 2 || min(x,y) < 1 || max(x,y)>N) {
			
			cerr << "error on line #"<<i+2<<" < "<<x<<" , "<<y<<">"<<endl;
			
			return 0;
		}
		adj[x].push_back(y);
		rev[y].push_back(x);
	}
	return 1;
}

int loadAnswer (FILE *inf) {
	if(fscanf(inf, "%d", &C) != 1 || C < 1 || C > N) return 0;
	if(fscanf(inf, "%d", &A) != 1 || A < 0 || A > N || A > 1000) return 0;
	return 1;
}


const int FullScore = 10;
const int LoScore = 2;
const int MidScore = 4;


void loadOutput(FILE* inf) {
	if(fscanf(inf, "%d", &user_C) != 1) {
		fclose(inf);
		exit(grade(0, "Format Error!"));
	}
	if(user_C != C) {
		fclose(inf);
		exit(grade(0, "Wrong Answer!"));
	}

	if(fscanf(inf, "%d", &user_A) != 1) {
		fclose(inf);
		exit(grade(LoScore, "<C> Correct but <A> not found! "));
	}
	if(user_A != A) {
		fclose(inf);
		exit(grade(LoScore, "<C> Correct but <A> Wrong!"));
	}
	
	int x, y;
	for(int i=0;i<A;++i) {
		if(fscanf(inf, "%d %d", &x, &y) != 2 || min(x, y) < 1 || max(x, y) > N) {
			fclose(inf);
			exit(grade(MidScore, "<C> and <A> Correct! Solution Format Error!"));
		}
		adj[x].push_back(y);
		rev[y].push_back(x);
	}
	fclose(inf);
	
	if(check())
		grade(10, "Correct!");
	else
		grade(MidScore, "<C> and <A> Correct but Wrong Solution!");
	
	exit(0);
}

int main(int argc,char *argv[]) {
	inFile = argv[1];
	outFile = argv[2];
	ansFile = argv[3];
	scoreFile = argv [5];
	logFile = argv[6];
	grade(0, "Internal Error!");

	FILE *file = fopen(inFile, "r");
	if(file == NULL) exit(grade(0, "No Input File!"));
	if(!loadInput(file)) {
		fclose(file);
		exit(grade(0, "Input File Format Error!"));
	}
	fclose(file);
	
	file = fopen(ansFile, "r");
	if(file == NULL) exit(grade(0, "No Answer File!"));
	if(!loadAnswer(file)){
		fclose(file);
		exit(grade(0, "Answer File Format Error!"));
	}
	fclose(file);
	
	file = fopen(outFile, "r");
	if(file == NULL) exit(grade(0, "No Output File!"));
	loadOutput(file);
}

Code】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10005;
const int MAXM = 200005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
bitset <MAXN> arr[MAXN];
vector <int> a[MAXN], sub[MAXN];
int n, m, top, Max, Stack[MAXN];
int timer, low[MAXN], dfn[MAXN];
int tot, belong[MAXN];
int x [MAXM], y [MAXM];
bool instack[MAXN];
vector <int> inpos, outpos;
bool inused[MAXN], outused[MAXN];
bool inp[MAXN], outp[MAXN];
void getarr(int pos) {
	arr[pos].set(pos);
	for (unsigned i = 0; i < sub[pos].size(); i++) {
		int point = sub[pos][i];
		for (unsigned j = 0; j < a[point].size(); j++) {
			int tmp = belong[a[point][j]];
			if (arr[tmp][tmp] == true) arr[pos] |= arr[tmp];
			else {
				getarr(tmp);
				arr[pos] |= arr[tmp];
			}
		}
	}
}
void work(int pos) {
	dfn[pos] = low[pos] = ++timer;
	instack[pos] = true;
	Stack[++top] = pos;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (dfn[a[pos][i]] == 0) {
			work(a[pos][i]);
			chkmin(low[pos], low[a[pos][i]]);
		} else if (instack[a[pos][i]]) chkmin(low[pos], dfn[a[pos][i]]);
	if (low[pos] == dfn[pos]) {
		to++;
		int cnt = 0, tmp = 0;
		while (tmp != pos) {
			cnt++;
			tmp = Stack[top--];
			belong[tmp] = tot;
			sub[tot].push_back(tmp);
			instack[tmp] = false;
		}
		chkmax(Max, cnt);
	}
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]);
		a[x[i]].push_back(y[i]);
	}
	for (int i = 1; i <= n; i++)
		if (dfn[i] == 0) work(i);
	writeln(Max);
	for (int i = 1; i <= m; i++)
		if (belong[x[i]] != belong[y[i]]) {
			outp[belong[x[i]]] = true;
			inp[belong[y[i]]] = true;
		}
	int cntin = 0, cntout = 0;
	for (int i = 1; i <= tot; i++) {
		if (outp[i] == false) {
			cntout++;
			outpos.push_back(i);
		}
		if (inp[i] == false) {
			cntin++;
			inpos.push_back(i);
		}
		arr[i].reset();
	}
	writeln(max(cntin, cntout));
	if (max(cntin, cntout) == 0) return 0;
	for (int i = 1; i <= tot; i++)
		if (arr[i][i] == false) getarr(i);
	int last = -1;
	for (unsigned i = 0; i < inpos.size(); i++)
	for (unsigned j = 0; j < outpos.size(); j++) {
		if (!inused[i] && !outused[j] && arr[inpos[i]][outpos[j]]) {
			inused[i] = outused[j] = true;
			if (last != -1) printf("%d %d\n", sub[outpos[last]][0], sub[inpos[i]][0]);
			last = j;
		}
	}
	printf("%d %d\n", sub[outpos[last]][0], sub[inpos[0]][0]);
	for (unsigned i = 0; i < inpos.size(); i++)
	for (unsigned j = 0; j < outpos.size(); j++) {
		if (!inused[i] && !outused[j]) {
			inused[i] = outused[j] = true;
			printf("%d %d\n", sub[outpos[j]][0], sub[inpos[i]][0]);
		}
	}
	int PointOnCircle = sub[inpos[0]][0];
	for (unsigned i = 0; i < inpos.size(); i++)
		if (!inused[i]) printf("%d %d\n", PointOnCircle, sub[inpos[i]][0]);
	for (unsigned i = 0; i < outpos.size(); i++)
		if (!outused[i]) printf("%d %d\n", sub[outpos[i]][0], PointOnCircle);
	return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324816149&siteId=291194637