【CodeForces】AIM Tech Round 5 (Div. 1 + Div. 2) 题解

【比赛链接】

【题解链接】

**【A】**Find Square

【思路要点】

  • 答案即为所有黑色方格坐标的平均值。
  • 时间复杂度 O ( N ∗ M ) O(N*M) O(NM)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
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, sx, sy, cnt;
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++) {
		static char s[MAXN];
		scanf("\n%s", s + 1);
		for (int j = 1; j <= m; j++)
			if (s[j] == 'B') {
				cnt++;
				sx += i;
				sy += j;
			}
	}
	printf("%d %d\n", sx / cnt, sy / cnt);
	return 0;
}

**【B】**Unnatural Conditions

【思路要点】

  • 我们可以构造两个和为 1 0 1000 10^{1000} 101000的数,使得它们能够回答任何输入。
  • A = 44444...445 , B = 55555...555 A=44444...445,B=55555...555 A=44444...445,B=55555...555即可。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
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 main() {
	string a, b;
	for (int i = 1; i <= 1000; i++) {
		a += '4';
		b += '5';
	}
	a += '5', b += '5';
	cout << a << endl;
	cout << b << endl;
	return 0;
}

**【C】**Rectangles

【思路要点】

  • 用前/后缀和计算矩形的交,就可以在 O ( N ) O(N) O(N)的时间内得出除去任何一个矩形后其余矩形的交。
  • 枚举去掉的矩形,若剩余矩形的交非空,输出其中任意一点。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int INF = 1e9;
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 {int x, y; };
point min(point a, point b) {return (point) {min(a.x, b.x), min(a.y, b.y)}; }
point max(point a, point b) {return (point) {max(a.x, b.x), max(a.y, b.y)}; }
point l[MAXN], r[MAXN], prel[MAXN], prer[MAXN], sufl[MAXN], sufr[MAXN];
int n;
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		read(l[i].x), read(l[i].y);
		read(r[i].x), read(r[i].y);
	}
	prel[0] = sufl[n + 1] = (point) {-INF, -INF};
	prer[0] = sufr[n + 1] = (point) {INF, INF};
	for (int i = 1; i <= n; i++) {
		prel[i] = max(prel[i - 1], l[i]);
		prer[i] = min(prer[i - 1], r[i]);
	}
	for (int i = n; i >= 1; i--) {
		sufl[i] = max(sufl[i + 1], l[i]);
		sufr[i] = min(sufr[i + 1], r[i]);
	}
	for (int i = 1; i <= n; i++) {
		point tl = max(prel[i - 1], sufl[i + 1]);
		point tr = min(prer[i - 1], sufr[i + 1]);
		if (tl.x <= tr.x && tl.y <= tr.y) {
			printf("%d %d\n", tl.x, tl.y);
			return 0;
		}
	}
	return 0;
}

**【D】**Order book

【思路要点】

  • 考虑一个 A c c e p t Accept Accept操作带来的影响,被 A c c e p t Accept Accept o f f e r offer offer(令其价格为 x x x)的类型不能由此次操作确定,但当时存在的价格大于 x x x o f f e r offer offer必须为 s e l l sell sell,当时存在的价格小于 x x x o f f e r offer offer必须为 b u y buy buy
  • 在最后一个 A c c e p t Accept Accept操作前的 A d d Add Add操作加进的 o f f e r offer offer在上述过程考虑结束后若必须为 s e l l / b u y sell/buy sell/buy,则方案数不变;若同时必须是 s e l l sell sell b u y buy buy,则不存在合法方案;否则,它既可以是 s e l l sell sell,也可以是 b u y buy buy,方案数应当乘以 2 2 2(注意此时这个 o f f e r offer offer一定被 A c c e p t Accept Accept了)。
  • 在最后一个 A c c e p t Accept Accept操作后的 A d d Add Add操作需要单独考虑,若一个 o f f e r offer offer被加入时已经小于某一个 b u y o f f e r buyoffer buyoffer,或大于某一个 s e l l o f f e r selloffer selloffer,那么它的类型时确定的,否则,它的类型是不确定的。记不确定的 o f f e r offer offer个数为 c n t cnt cnt,答案应当再乘以 c n t + 1 cnt+1 cnt+1
  • s t d : : p r i o r i t y _ q u e u e std::priority\_queue std::priority_queue来模拟上述过程,时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 400005;
const int P = 1e9 + 7;
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("");
}
bool type[MAXN]; int x[MAXN];
map <int, bool> black, white, exist;
priority_queue <int> hb, hw;
int main() {
	int n; read(n);
	int last = 0;
	for (int i = 1; i <= n; i++) {
		char opt[15];
		scanf("\n%s%d", opt + 1, &x[i]);
		type[i] = opt[2] == 'D';
		if (!type[i]) last = i, exist[x[i]] = true;
	}
	for (int i = 1; i <= n; i++)
		if (type[i]) {
			hb.push(-x[i]);
			hw.push(x[i]);
		} else {
			while (!hb.empty() && hb.top() >= -x[i]) {
				if (hb.top() != -x[i]) black[-hb.top()] = true;
				hb.pop();
			}
			while (!hw.empty() && hw.top() >= x[i]) {
				if (hw.top() != x[i]) white[hw.top()] = true;
				hw.pop();
			}
		}
	int ans = 1, Max = 0, Min = 1e9;
	for (int i = 1; i <= last; i++)
		if (type[i]) {
			int tmp = 0;
			tmp += !black[x[i]];
			if (black[x[i]] && !exist[x[i]]) chkmax(Max, x[i]);
			tmp += !white[x[i]];
			if (white[x[i]] && !exist[x[i]]) chkmin(Min, x[i]);
			ans = ans * tmp % P;
		}
	int cnt = 1;
	for (int i = last + 1; i <= n; i++)
		if (x[i] >= Max && x[i] <= Min) cnt++;
	writeln(1ll * ans * cnt % P);
	return 0;
}

**【E】**Restore Array

【思路要点】

  • 我们首先考虑所有数都相等的情况。
  • 若所有数都等于 0 0 0,那么任意一组所有数都相同的解都可以作为答案。
  • 若所有数都相等,且不为 0 0 0,那么问题无解。
  • 接下来我们认为至少存在两个数不等。
  • 注意到 ( a + b ) % b = a ( b &gt; a ) (a+b)\%b=a(b&gt;a) (a+b)%b=a(b>a)
  • M a x = m a x { b i } Max=max\{b_i\} Max=max{bi},取 b i b_i bi使得 b i = M a x , b i − 1 ≠ M a x b_i=Max,b_{i-1}\ne Max bi=Max,bi1̸=Max,令 a i = b i a_i=b_i ai=bi
  • b i − 1 = 0 b_{i-1}=0 bi1=0,令 a i − 1 = 2 ∗ a i a_{i-1}=2*a_i ai1=2ai,否则令 a i − 1 = a i + b i − 1 a_{i-1}=a_i+b_{i-1} ai1=ai+bi1
  • 对于剩余所有 j ( j ≠ i , i − 1 ) j(j\ne i,i-1) j(j̸=i,i1),令 a j = a j + 1 + b j a_j=a_{j+1}+b_j aj=aj+1+bj即可。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 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("");
}
int n, Max, a[MAXN];
long long ans[MAXN];
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]), chkmax(Max, a[i]);
	if (Max == 0) {
		printf("YES\n");
		for (int i = 1; i <= n; i++)
			printf("%d ", 1);
		printf("\n");
		return 0;
	}
	a[0] = a[n];
	for (int i = 1; i <= n; i++)
		if (a[i] == Max && a[i - 1] != Max) {
			long long now = a[i];
			int pos = i - 1; ans[i] = now;
			if (pos == 0) pos = n;
			while (pos != i) {
				if (a[pos] == 0 && now == a[i]) now += a[i];
				else now += a[pos];
				ans[pos] = now;
				if (--pos == 0) pos = n;
			}
			printf("YES\n");
			for (int i = 1; i <= n; i++)
				write(ans[i]), putchar(' ');
			printf("\n");
			return 0;
		}
	printf("NO\n");
	return 0;
}

**【F】**Make Symmetrical

【思路要点】

  • 两个关于过原点的某条直线对称的点到原点距离相等。
  • 而不定方程 x 2 + y 2 = C ( C ≤ 1 0 12 ) x^2+y^2=C(C≤10^{12}) x2+y2=C(C1012)的非负整数解的个数至多有 M = 144 M=144 M=144个。
  • 对每个点按照到原点的距离分类,在插入或删除时暴力维护即可。
  • 时间复杂度 O ( Q ∗ M ∗ L o g V ) O(Q*M*LogV) O(QMLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
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 {int x, y; };
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}; }
point operator / (point a, int b) {return (point) {a.x / b, a.y / b}; }
long long moo(point a) {return a.x * a.x + a.y * a.y; }
bool operator < (point a, point b) {
	if (a.x == b.x) return a.y < b.y;
	else return a.x < b.x;
}
int gcd(int x, int y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}
int n, tot, cnt;
map <point, int> ans;
map <long long, int> mp;
vector <point> a[MAXN];
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		int opt; point tmp;
		read(opt), read(tmp.x), read(tmp.y);
		if (opt == 1) {
			cnt++;
			long long tnp = moo(tmp);
			if (mp[tnp] == 0) mp[tnp] = ++tot;
			int pos = mp[tnp];
			ans[tmp / gcd(tmp.x, tmp.y)]++;
			for (unsigned j = 0; j < a[pos].size(); j++) {
				point sum = tmp + a[pos][j];
				sum = sum / gcd(sum.x, sum.y);
				ans[sum] += 2;
			}
			a[pos].push_back(tmp);
		}
		if (opt == 2) {
			cnt--;
			long long tnp = moo(tmp);
			if (mp[tnp] == 0) mp[tnp] = ++tot;
			int pos = mp[tnp];
			ans[tmp / gcd(tmp.x, tmp.y)]--;
			for (unsigned j = 0; j < a[pos].size(); j++)
				if (tmp.x == a[pos][j].x && tmp.y == a[pos][j].y) {
					a[pos].erase(a[pos].begin() + j);
					break;
				}
			for (unsigned j = 0; j < a[pos].size(); j++) {
				point sum = tmp + a[pos][j];
				sum = sum / gcd(sum.x, sum.y);
				ans[sum] -= 2;
			}
		}
		if (opt == 3) writeln(cnt - ans[tmp / gcd(tmp.x, tmp.y)]);
	}
	return 0;
}

**【G】**Guess the number

【思路要点】

  • d p i , j dp_{i,j} dpi,j表示剩余 i i i次询问的机会,当前确定了 k ≥ j k≥j kj的情况下,能够确定的最大长度。

  • 设计出状态之后,是容易转移的:

    d p i , j = d p i − 1 , j + f ( i − 1 , i , j + 1 + d p i − 1 , j ) dp_{i,j}=dp_{i-1,j}+f(i-1,i,j+1+dp_{i-1,j}) dpi,j=dpi1,j+f(i1,i,j+1+dpi1,j)

    $f(k,i,j)=\left{\begin{array}{rcl}0 & & {i=0}\1+dp_{k,j}+f(k,i-1,j+1+dp_{k,j}) & & {i\ne 0}\end{array} \right. $

  • 并且注意到当 j ≥ 10000 j≥10000 j10000 d p i , j = d p i , 10000 dp_{i,j}=dp_{i,10000} dpi,j=dpi,10000,因此我们可以利用该性质对上述过程进行剪枝。

  • d p 5 , 1 dp_{5,1} dp5,1恰好为$10004205361450474 $,说明题目给出的限制实际上是最紧的。

  • 利用 d p dp dp的结果进行交互即可。

  • 时间复杂度 O ( 5 ∗ 1000 0 2 ) O(5*10000^2) O(5100002),实际运行复杂度很不满。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10005;
const int MAXM = 10;
const long long INF = 1e18;
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("");
}
long long dp[MAXM][MAXN], a[MAXN];
long long times(long long a, long long b) {
	if ((long double) a * b >= INF) return INF;
	else return a * b;
}
int main() {
	for (int i = 1; i <= 5; i++)
	for (int j = 1; j <= 10000; j++) {
		long long ans = -1, pos = j;
		for (int k = 0; k <= j; k++) {
			ans += dp[i - 1][pos] + 1;
			chkmin(ans, INF);
			pos += dp[i - 1][pos] + 1;
			if (pos >= 10000) {
				ans += times(dp[i - 1][10000] + 1, j - k);
				chkmin(ans, INF);
				break;
			}
		}
		dp[i][j] = ans;
	}
	int cnt = 5; long long pos = 1;
	while (true) {
		int tmp = min(pos, 10000ll), tnp = tmp;
		cout << tmp; a[0] = pos - 1;
		for (int i = 1; i <= tmp; i++) {
			pos += dp[cnt - 1][tnp];
			tnp = min(tnp + dp[cnt - 1][tnp] + 1, 10000ll);
			a[i] = pos; pos++;
			cout << ' ' << a[i];
		}
		cout << endl;
		read(tnp);
		if (tnp == -1) return 0;
		cnt--; pos = a[tnp] + 1;
	}
	return 0;
}

**【H】**Make Square

【思路要点】

  • 首先我们可以将所有数的完全平方因子除去,只剩下每个质因数至多一种。
  • 由此我们也可以看出,两种操作实际上是本质相同的,考虑只使用除法操作。
  • 将两个数 A , B A,B A,B的乘积修改为完全平方数的过程可以看做对 A , B A,B A,B各进行若干次除法操作,使得它们等于同一个数 C C C
  • 考虑离线询问,从左到右枚举询问的右端点 i i i,并维护数组 M a x C , c n t Max_{C,cnt} MaxC,cnt,表示在前 i − 1 i-1 i1个数中,进行 c n t cnt cnt次除法操作可以得到 C C C的最靠右的数的位置;再维护数组 r c n t r_{cnt} rcnt表示使得答案为 c n t cnt cnt的最靠右的左端点,显然有了 r r r数组后是容易回答询问的。
  • 对于每一个右端点 i i i,考虑 r r r数组的变化,枚举 A i A_i Ai的因数 C C C,再枚举与 A i A_i Ai配为完全平方数的 B B B得到 C C C的操作次数,即可借助 M a x Max Max数组更新 r r r数组,对于 M a x Max Max数组的更新类似。
  • 时间复杂度 O ( N ∗ A i ∗ M a x A n s + Q ∗ M a x A n s ) O(N*\sqrt{A_i}*MaxAns+Q*MaxAns) O(NAi MaxAns+QMaxAns),其中 M a x A n s = 11 MaxAns=11 MaxAns=11

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXQ = 2e6 + 5;
const int MAXV = 6e6 + 5;
const int Maxans = 12;
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, q, a[MAXN], ans[MAXQ];
int r[Maxans + 5], Max[MAXV][Maxans + 5];
int tot, prime[MAXV], f[MAXV], cnt[MAXV];
vector <int> pos[MAXN], home[MAXN];
void init() {
	for (int i = 2; i < MAXV; i++) {
		if (f[i] == 0) prime[++tot] = f[i] = i, cnt[i] = 1;
		for (unsigned j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp >= MAXV) break;
			f[tmp] = prime[j];
			cnt[tmp] = cnt[i] + 1;
		}
	}
}
int process(int x) {
	int ans = 1;
	for (int i = 1; prime[i] * prime[i] <= x; i++)
		if (x % prime[i] == 0) {
			int cnt = 0;
			while (x % prime[i] == 0) x /= prime[i], cnt++;
			if (cnt & 1) ans *= prime[i];
		}
	return ans * x;
}
int main() {
	read(n), read(q);
	init();
	for (int i = 1; i <= n; i++) {
		read(a[i]);
		a[i] = process(a[i]);
	}
	for (int i = 1; i <= q; i++) {
		int x, y; read(x), read(y);
		pos[y].push_back(x);
		home[y].push_back(i);
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j * j <= a[i]; j++) {
			if (a[i] % j == 0) {
				int now = cnt[a[i] / j];
				for (int k = 0; k + now <= Maxans; k++)
					chkmax(r[k + now], Max[j][k]);
				now = cnt[j];
				for (int k = 0; k + now <= Maxans; k++)
					chkmax(r[k + now], Max[a[i] / j][k]);
			}
		}
		for (int j = 1; j * j <= a[i]; j++) {
			if (a[i] % j == 0) {
				chkmax(Max[j][cnt[a[i] / j]], i);
				chkmax(Max[a[i] / j][cnt[j]], i);
			}
		}
		for (unsigned j = 0; j < pos[i].size(); j++) {
			int tans = Maxans;
			for (int k = 0; k <= Maxans; k++)
				if (r[k] >= pos[i][j]) chkmin(tans, k);
			ans[home[i][j]] = tans;
		}
	}
	for (int i = 1; i <= q; i++)
		writeln(ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值