【CodeForces】CodeForces Round #549 (Div. 1) 题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/89035342

【比赛链接】

【题解链接】

【A】 The Beatles

【思路要点】

  • 任取一个合法的起始点,枚举所有可能的步长 l l ,计算步数 N × k g c d ( N k , l ) \frac{N\times k}{gcd(Nk,l)} ,取最优值即可。
  • 时间复杂度 O ( N L o g V ) O(NLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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("");
}
ll gcd(ll x, ll y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}
ll n, k, a, b, Max, Min;
void work(ll x) {
	for (int i = 1; i <= n; i++) {
		chkmax(Max, gcd(x, n * k));
		chkmin(Min, gcd(x, n * k));
		x += k;
	}
}
int main() {
	read(n), read(k);
	read(a), read(b);
	if (a < b) swap(a, b);
	Max = 1, Min = n * k;
	work(a - b), work(k - a - b);
	write(n * k / Max), putchar(' '), writeln(n * k / Min);
	return 0;
}

【B】 Lynyrd Skynyrd

【思路要点】

  • 不妨令给定的排列为 ( 1 , 2 , 3 , . . . , N ) (1,2,3,...,N)
  • 对于一个序列中的位置 a i a_i ,令 j j 是最小的满足 j &gt; i , a j = a i % N + 1 j&gt;i,a_j=a_i\%N+1 的位置,连边 i j i\rightarrow j ,不难发现询问等价于询问区间内最长链的长度是否达到 N N
  • 倍增找到每一个位置出发走 N 1 N-1 步到达的位置,再利用前缀和 O ( 1 ) O(1) 询问即可。
  • 时间复杂度 O ( N + M L o g N + Q ) O(N+MLogN+Q)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXLOG = 20;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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("");
}
int n, m, q, a[MAXN], nxt[MAXN], last[MAXN];
int Max[MAXN], travel[MAXN][MAXLOG];
int main() {
	read(n), read(m), read(q);
	for (int i = 1; i <= n; i++)
		read(a[i]), nxt[a[i]] = a[i - 1];
	nxt[a[1]] = a[n];
	for (int i = 1; i <= m; i++) {
		int x; read(x);
		travel[i][0] = last[nxt[x]];
		for (int j = 1; j < MAXLOG; j++)
			travel[i][j] = travel[travel[i][j - 1]][j - 1];
		int pos = i;
		for (int j = 0; j < MAXLOG; j++)
			if ((1 << j) & (n - 1)) pos = travel[pos][j];
		Max[i] = max(Max[i - 1], pos);
		last[x] = i;
	}
	for (int i = 1; i <= q; i++) {
		int x, y; read(x), read(y);
		if (Max[y] >= x) putchar('1');
		else putchar('0');
	}
	return 0;
}

【C】 U2

【思路要点】

  • 单独考虑每一个点 ( x , y ) (x,y) 对抛物线 ( b , c ) (b,c) 的限制,有 x 2 + b x + c y x^2+bx+c\geq y
  • 该限制显然是一个半平面,由于确定一条抛物线需要两个点,我们需要计算的是半平面交上顶点的个数。
  • 由于本题值域较大,而半平面交需要计算直线交点,亲测直接采用半平面交难以通过。
  • 考虑对上式进行变形,即变形为 b x + c y x 2 bx+c\geq y-x^2
  • 注意到不等式左侧是一条直线,若将 ( x , y x 2 ) (x,y-x^2) 看成一个点, 不等式的限制等价于直线 b x + c bx+c 需要高过点 ( x , y x 2 ) (x,y-x^2) ,因此直接求点集 ( x , y x 2 ) (x,y-x^2) 的上凸壳即可。
  • 时间复杂度 O ( N L o g N ) O(NLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
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("");
}
struct point {ll x, y; } a[MAXN];
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; }
long long operator * (point a, point b) {return 1ll * a.x * b.y - 1ll * a.y * b.x; }
bool operator < (point a, point b) {
	if (a.x == b.x) return a.y < b.y;
	else return a.x > b.x;
}
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++) {
		ll x, y; read(x), read(y);
		a[i] = (point) {x, y - x * x};
	}
	sort(a + 1, a + n + 1);
	int top = 1;
	for (int i = 2; i <= n; i++) {
		if (a[top].x == a[i].x) top--;
		while (top >= 2 && (a[top] - a[top - 1]) * (a[i] - a[top - 1]) <= 0) top--;
		a[++top] = a[i];
	}
	printf("%d\n", top - 1);
	return 0;
}

【D】 Foreigner

【思路要点】

  • 我们需要为 i n a d e q u a t e inadequate 数设计一个状态,使得我们可以方便地判断在某个 i n a d e q u a t e inadequate 数后增加一个字符后得到的是不是 i n a d e q u a t e inadequate 数,并进一步得到其状态。
  • 本题较为关键的一点在于 1 + 2 + 3 + + 10 = 55 0   ( m o d   11 ) 1+2+3+\dots+10=55\equiv0\ (mod\ 11) ,因此若我们在状态中记录当前 i n a d e q u a t e inadequate 数的排名 r n k rnk ,长度不足该数的 i n a d e q u a t e inadequate 数的个数 l s t lst ,相同长度的 i n a d e q u a t e inadequate 数的个数 c u r cur 三数在模 11 11 意义下的结果,就可以达到上述目的。
  • 此后,我们可以搜出所有可行的状态数 S S ,并建立一个大小为 S S 的自动机。
  • 在自动机上简单 d p dp 即可得到各个位置出发的最长 i n a d e q u a t e inadequate 数的长度。
  • 时间复杂度 O ( N S ) O(NS) ,其中 S = 31 S=31

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXS = 105;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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("");
}
int tot, num[11][11][11];
int trans[MAXS][10];
void dfs(int rnk, int lst, int cur) {
	if (num[rnk][lst][cur]) return;
	num[rnk][lst][cur] = ++tot;
	int from = (lst + cur) % 11;
	for (int i = (lst + 1) % 11; i != rnk; i = (i + 1) % 11)
		from = (from + i) % 11;
	int nxt = 0;
	for (int i = lst; i != (lst + cur) % 11; i = (i + 1) % 11)
		nxt = (nxt + i + 1) % 11;
	for (int i = 1; i <= rnk; i++) {
		dfs((from + i) % 11, (lst + cur) % 11, nxt);
		trans[num[rnk][lst][cur]][i - 1] = num[(from + i) % 11][(lst + cur) % 11][nxt];
	}
}
void initstates() {
	for (int i = 1; i <= 9; i++)
		dfs(i, 0, 9);
	//cerr << tot << endl;
}
char s[MAXN]; int n;
int dp[MAXN][MAXS];
int main() {
	initstates();
	scanf("%s", s + 1);
	n = strlen(s + 1);
	ll ans = 0;
	for (int i = n; i >= 1; i--) {
		if (i == n) {
			for (int j = 1; j <= tot; j++)
				dp[i][j] = i;
		} else {
			for (int j = 1; j <= tot; j++) {
				if (trans[j][s[i + 1] - '0']) dp[i][j] = dp[i + 1][trans[j][s[i + 1] - '0']];
				else dp[i][j] = i;
			}
		}
		if (s[i] != '0') {
			int now = dp[i][num[s[i] - '0'][0][9]];
			ans += now - i + 1;
		}
	}
	writeln(ans);
	return 0;
}

【E】 Pink Floyd

【思路要点】

  • 考虑 M = 0 M=0 的做法,我们只需要每次询问一对没有删去的点,删去被指向的点即可。
  • M &gt; 0 M&gt;0 时,我们找到的一对没有删去的点之间可能已经存在粉边,我们需要一些改进。
  • 将给出的粉图求强联通分量,并缩点,我们会得到一系列入度为 0 0 的强联通分量,若只有一个入度为 0 0 的强联通分量,那么输出其中任意一点即可;否则,找到所属入度为 0 0 的强联通分量不同的两点,并套用 M = 0 M=0 的做法,由于这两个强联通分量入度均为 0 0 ,因此这两点之间一定不存在粉边。
  • 当一个强联通分量被删完后,将其删去,并断开其连出的边,检查是否有新的入度为 0 0 的强联通分量产生即可。
  • 时间复杂度 O ( N + M ) O(N+M) ,询问次数不超过 N N

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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("");
}
int n, m, timer, dfn[MAXN], low[MAXN];
int tot, belong[MAXN], top, Stack[MAXN];
vector <int> a[MAXN], b[MAXN], s[MAXN];
bool instack[MAXN]; int d[MAXN];
void tarjan(int pos) {
	dfn[pos] = low[pos] = ++timer;
	Stack[++top] = pos;
	instack[pos] = true;
	for (auto x : a[pos]) {
		if (!dfn[x]) {
			tarjan(x);
			chkmin(low[pos], low[x]);
		} else if (instack[x]) chkmin(low[pos], dfn[x]);
	}
	if (low[pos] == dfn[pos]) {
		int tmp = Stack[top--];
		instack[tmp] = false;
		belong[tmp] = ++tot;
		while (tmp != pos) {
			tmp = Stack[top--];
			instack[tmp] = false;
			belong[tmp] = tot;
		}
	}
	
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		a[x].push_back(y);
	}
	for (int i = 1; i <= n; i++) {
		if (!dfn[i]) tarjan(i);
		s[belong[i]].push_back(i);
		for (auto x : a[i])
			if (belong[i] != belong[x]) {
				b[belong[i]].push_back(belong[x]);
				d[belong[x]]++;
			}
	}
	static int q[MAXN];
	int l = 0, r = -1;
	for (int i = 1; i <= tot; i++)
		if (d[i] == 0) q[++r] = i;
	while (l < r) {
		int x = q[l++], y = q[l++];
		cout << "? " << s[x].back() << ' ' << s[y].back() << endl;
		bool res; read(res);
		if (res) {
			q[--l] = x;
			s[y].pop_back();
			if (s[y].empty()) {
				for (auto p : b[y])
					if (--d[p] == 0) q[++r] = p;
			} else q[--l] = y;
		} else {
			q[--l] = y;
			s[x].pop_back();
			if (s[x].empty()) {
				for (auto p : b[x])
					if (--d[p] == 0) q[++r] = p;
			} else q[--l] = x;
		}
	}
	cout << "! " << s[q[l]].back() << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/89035342
今日推荐