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;
}