AtCoder Beginner Contest 250[题解D~G]

\(ABC250\)

\(D\)

题意

一个合法的整数 \(k\) 需要满足以下条件:

  • \(k = p \times q ^ 3\),其中 \(p\)\(q\) 都是质数,且 \(p < q\)

给定一个整数 \(n\),求有多少小于等于 \(n\) 的整数合法。

\(1\leq n\leq 1\times 10 ^ {18}\)

\(Sol\)

不难看出最多只需要处理出 \(10^6\) 以内的质数。

打表不难发现每一种 \(q\) 的取值对应的合法整数是不会冲突的。

枚举 \(q\) ,二分 \(p\) 的取值范围即可。

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10, M = 1e6 + 10, lim = 1e6;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
int n, ans;
int cnt, pri[N];
bool vis[M];
inline void initial()
{
	for(register int i = 2; i <= lim; i++){
		if(!vis[i]){
			pri[++cnt] = i;
			for(register int j = 2; i * j <= lim; j++) vis[i * j] = true;
		}
	}
}
signed main()
{
	initial();
	n = read();
	for(register int i = 2; i <= cnt; i++){
		int x = pri[i] * pri[i] * pri[i], y = n / x;
		int l = 1, r = cnt, res = -1;
		while(l <= r){
			int mid = (l + r) >> 1;
			if(pri[mid] <= y && pri[mid] < pri[i]) res = mid, l = mid + 1;
			else r = mid - 1;
		}
		if(res == -1) break;
		ans = ans + res;
	}
	cout << ans << "\n";
	return 0;
}

\(E\)

题意

给定两个长度为 \(n\) 的整数数组 \(a_i,b_i\)

\(Q\) 次询问,每次询问给出两个数 \(x,y\),若 \((a_1,a_2,a_3……a_x)\)\((b_1,b_2,b_3……b_n)\) 中所包含的元素种类相同,输出 \(Yes\),否则输出 \(No\)

\(1\leq N,Q\leq 2\times 10 ^ 5,1\leq a_i,b_i\leq 10^9,1\leq x_i,y_i\leq N\)

\(Sol\)

我的做法:

比较显然可以通过莫队暴力移动。

离散化后,记录一下数组 \(a_i\)\(b_i\) 中当前区间各个元素的数量,要么移除要么添加,视情况维护一个 \(cnt\) 表示两个数组中不同元素的数量。

何老师的做法:

维护前缀 \(1、2、3\) 次方和,全部相等输出 \(Yes\),否则输出 \(No\),注意出现重复元素仅贡献一次。

基本上卡不掉的区间 \(hash\),代码异常简单。

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e5 + 10, M = 1e6 + 10, lim = 1e6;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
struct node{
	int x, y, id;
}que[N];
int n, Q, siz, cnt, flag;
int a[N], b[N], pos[N];
int cnta[N], cntb[N]; //记录元素个数 
int top, sck[N];
bool ans[N];
map<int, int> mp;
inline bool cmp(node X, node Y)
{
	if(pos[X.x] == pos[Y.x]){
		if(pos[X.x] & 1) return X.y > Y.y;
		else return X.y < Y.y;
	}
	return X.x < Y.x;
}
signed main()
{
	n = read();
	for(register int i = 1; i <= n; i++) a[i] = read(), sck[++top] = a[i];
	for(register int i = 1; i <= n; i++) b[i] = read(), sck[++top] = b[i];
	sort(sck + 1, sck + top + 1);
	top = unique(sck + 1, sck + top + 1) - sck - 1;
	//离散化 
	for(register int i = 1; i <= top; i++) mp[sck[i]] = i;
	for(register int i = 1; i <= n; i++) a[i] = mp[a[i]], b[i] = mp[b[i]];
	// 莫队分块 
	siz = sqrt(n);
	for(register int i = 1; i <= n; i++) pos[i] = 1 + (i - 1) / siz;
	//莫堆排序 
	Q = read();
	for(register int i = 1; i <= Q; i++)
		que[i].x = read(), que[i].y = read(), que[i].id = i; 
	sort(que + 1, que + Q + 1, cmp);
	//运算 
	int l = 0, r = 0;
	for(register int i = 1; i <= Q; i++){
		//a 数组 
		if(que[i].x > l){
			for(register int j = l + 1; j <= que[i].x; j++){
				cnta[a[j]]++;
				if(cnta[a[j]] == 1 && !cntb[a[j]]) flag++;
				if(cnta[a[j]] == 1 && cntb[a[j]]) flag--;
			}
		}
		if(que[i].x < l){
			for(register int j = l; j > que[i].x; j--){
				cnta[a[j]]--;
				if(!cnta[a[j]] && cntb[a[j]]) flag++;
				if(!cnta[a[j]] && !cntb[a[j]]) flag--;
			}
		}
		//b 数组 
		if(que[i].y > r){
			for(register int j = r + 1; j <= que[i].y; j++){
				cntb[b[j]]++;
				if(cntb[b[j]] == 1 && !cnta[b[j]]) flag++;
				if(cntb[b[j]] == 1 && cnta[b[j]]) flag--;
			}
		}
		if(que[i].y < r){
			for(register int j = r; j > que[i].y; j--){
				cntb[b[j]]--;
				if(!cntb[b[j]] && cnta[b[j]]) flag++;
				if(!cntb[b[j]] && !cnta[b[j]]) flag--;
			}
		}
		l = que[i].x, r = que[i].y;
		if(!flag) ans[que[i].id] = true;
		else ans[que[i].id] = false;
	}
	for(register int i = 1; i <= Q; i++){
		if(ans[i]) puts("Yes");
		else puts("No");
	}
	return 0;
}

\(F\)

题意

给定 \(n\) 个点构成的凸多边形,要求只能切一刀,这一刀必须经过两个端点,切完后选择一块吃掉,设被吃掉的部分面积为 \(b\),总面积的 \(\frac{1}{4}\)\(a\),求 \(8\times |a - b|\) 的最小值,并且经过证明这是一个整数。

\(1\leq n\leq 1\times 10 ^5\)

\(Sol\)

做的时候读错题了,以为能够切无数刀,完全想不出来。

只能切一刀就挺好做了。

枚举第一个端点,不难发现每次想要多切一个端点就相当于增加了一个三角形,根据计算几何这个面积不难被求出。我们只需要让被我们切掉的面积尽可能接近 \(a\) 就行了。

但这是 \(O(n ^ 2)\) 的做法,实际上很容易实际上这个东西可以用一个类似滑动窗口 的东西维护,在上一个求得值的基础上,减去以上一个点,上一次最远端点,以及当前点组成的三角形的面积,然后再往后面继续做,这样就是 \(O(n)\) 的了。

这个 \(dp\) 真的蛮简单……

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w; 
}
struct Point{
	int x, y; 
	friend Point operator + (const Point &a, const Point &b){
		return (Point){a.x + b.x, a.y + b.y};
	}
	friend Point operator - (const Point &a, const Point &b){
		return (Point){a.x - b.x, a.y - b.y}; 
	}
	friend bool operator < (const Point &a, const Point &b){
		if(a.x < b.x) return true;
		if(a.x > b.x) return false;
		if(a.y < b.y) return true;
		return false;
	}
}p[N];
int n, s;
inline int crs(Point a, Point b) { return a.x * b.y - a.y * b.x; } //求三角形面积 
signed main()
{
	n = read();
	for(register int i = 1; i <= n; i++) 
		p[i].x = read(), p[i].y = read();
	for(register int i = 3; i <= n; i++)
		s += abs(crs(p[i - 1] - p[1], p[i] - p[1]));
	int res = 8e18, ans = 0;
	int r = 2;
	for(register int i = 1; i <= n; i++){
		while(4 * ans < s){
			int v = r + 1;
			if(v == n + 1) v = 1;
			ans += abs(crs(p[r] - p[i], p[v] - p[i]));
			r++;
			if(r == n + 1) r = 1;
			res = min(res, abs(4 * ans - s));
		}
		int v = i + 1;
		if(v == n + 1) v = 1;
		ans -= abs(crs(p[i] - p[r], p[v] - p[r]));
		res = min(res, abs(4 * ans - s));
	}
	printf("%lld\n", res);
	return 0;
}

\(G\)

题意

已知接下来 \(n\) 天的股票价格,每天你可以买进一股股票,卖出一股股票,或者什么也不做。 \(n\) 后你拥有的股票应该为 \(0\)

求最多赚多少钱。

\(1\leq n \leq 3\times 10 ^ 5\)

\(Sol\)

原题 \(CF865D\)

一个简单的贪心思路是从前往后遍历,买进小的股票,碰到大的就卖。

这显然是错的。

例如数据:

3
1 2 100

考虑加上一个反悔操作,对于一个形如 “在第 \(i\) 天买进,第 \(j\) 天卖出” 的决策,假设一个价值为 \(w\) 的物品,使得 “在第 \(i\) 天买进,第 \(j\) 天卖出,同时买入价值为 \(w\) 的物品,且在第 \(k\) 天卖出” 等价于 “在第 \(i\) 天买入,在第 \(k\) 天卖出”。

这是反悔贪心的一个经典操作,相当于加了一个撤回操作。

\(code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
int n, ans;
priority_queue<int, vector<int>, greater<int> > q;
signed main() {
	n = read();
	for(register int i = 1; i <= n; ++i) {
		int k = read();
		if(!q.empty() && q.top() < k) ans += k - q.top(), q.pop(), q.push(k);
		q.push(k);
	}
	printf("%lld\n", ans);
	return 0;
}
posted @ 2022-05-09 19:56  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(46)  评论(0编辑  收藏  举报