版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/81705040
【比赛链接】
【题解链接】
**【A】**Elections
【思路要点】
考虑枚举 号党派最终的得票,剩余部分我们可以通过贪心解决:
对于每一个得票数高于 号党派的党派,贪心地改变其代价最小的若干投票。
然后贪心地改变所有投票中代价最小的若干投票来补足 号党派的得票。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
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("");
}
int n, m, p[MAXN], c[MAXN];
vector <int> a[MAXN];
long long calc(int cnt) {
int lft = cnt - a[1].size();
long long ans = 0;
int tot = 0;
static int val[MAXN];
for (int i = 2; i <= m; i++) {
int tmp = 0;
if (a[i].size() >= cnt) tmp = a[i].size() - cnt + 1;
if (tmp > lft) return INF;
lft -= tmp;
for (unsigned j = 0; j < tmp; j++)
ans += a[i][j];
for (unsigned j = tmp; j < a[i].size(); j++)
val[++tot] = a[i][j];
}
sort(val + 1, val + tot + 1);
for (int i = 1; i <= lft; i++)
ans += val[i];
return ans;
}
int main() {
long long ans = INF;
read(n), read(m);
for (int i = 1; i <= n; i++) {
read(p[i]), read(c[i]);
a[p[i]].push_back(c[i]);
}
for (int i = 1; i <= m; i++)
sort(a[i].begin(), a[i].end());
for (int cnt = a[1].size(); cnt <= n; cnt++)
chkmin(ans, calc(cnt));
writeln(ans);
return 0;
}
**【B】**The hat
【思路要点】
- 将数列差分,我们得到的是 个 和 个 ,花费 次操作可以得到一个区间的和,我们需要判断是否存在圆环上连续的 个数和为0。
- 首先当 不是 的倍数,连续的 个数和显然为奇数,无解。
- 我们发现圆环上连续的两个长度为 的区间数字之和或相等,或相差 。
- 也就是说如果我们将圆环上长度为 的区间的数字之和依次排列,一个正数和一个负数之间一定有一个 。
- 取圆环的两半,它们的和必然都为 ,或者一正一负,二分找到 的位置即可。
- 时间复杂度 ,使用操作次数不超过 。
【代码】
#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;
int getans(int pos) {
cout << '?' << ' ' << pos << endl;
int x; cin >> x;
if (pos <= n / 2) cout << '?' << ' ' << pos + n / 2 << endl;
else cout << '?' << ' ' << pos - n / 2 << endl;
int y; cin >> y;
return x - y;
}
int main() {
cin >> n;
if (n % 4 == 2) {
cout << '!' << ' ' << -1 << endl;
return 0;
}
int l = 1, vl = getans(l), r = n / 2 + 1, vr = -vl;
if (vl == 0) {
cout << '!' << ' ' << l << endl;
return 0;
}
while (true) {
int mid = (l + r) / 2;
int tmp = getans(mid);
if (tmp == 0) {
cout << '!' << ' ' << mid << endl;
return 0;
}
if (tmp < 0 && vl < 0) l = mid, vl = tmp;
else if (tmp > 0 && vl > 0) l = mid, vl = tmp;
else r = mid, vr = tmp;
}
return 0;
}
**【C】**Sergey’s problem
【思路要点】
任取原图中一点 ,删去 以及 指向的点,递归构造剩余部分的答案 。
若 不被 中任意一个点指向,那么 即为原图的答案。
否则, 即为原图的答案。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
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, now;
bool vis[MAXN], del[MAXN];
vector <int> a[MAXN], ans;
void work() {
while (now <= n && del[now]) now++;
if (now > n) return;
int pos = now; del[pos] = true;
for (unsigned i = 0; i < a[pos].size(); i++)
del[a[pos][i]] = true;
work();
if (!vis[pos]) {
vis[pos] = true;
for (unsigned i = 0; i < a[pos].size(); i++)
vis[a[pos][i]] = true;
ans.push_back(pos);
}
}
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);
}
now = 1, work();
writeln(ans.size());
for (unsigned i = 0; i < ans.size(); i++)
printf("%d ", ans[i]);
return 0;
}
**【D】**Large Triangle
【思路要点】
- 若我们已经枚举了三角形的两个顶点,我们会希望剩余的点已经按照到这两个点确定的直线的距离排好了序,这样只需要简单二分即可解决问题。
- 那么只需要按照两点之间向量的极角序枚举点对,并且动态维护所有点到当前直线的偏序关系即可,当我们枚举到 时,由于题目保证了不存在三点共线, 和 在偏序关系中一定是相邻的两个点,在枚举后交换两点的排名即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
const int MAXM = 4e6 + 5;
const double pi = acos(-1);
const double eps = 1e-9;
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}; }
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.y == b.y) return a.x > b.x;
else return a.y < b.y;
}
struct posi {point pos; int num; };
struct info {double angle; int x, y; };
long long s; int n, home[MAXN];
posi a[MAXN]; info b[MAXM];
void solve(int x, int y) {
point delta = a[home[y]].pos - a[home[x]].pos;
int l = home[y] + 1, r = n;
while (l <= r) {
int mid = (l + r) / 2;
long long tmp = abs(delta * (a[mid].pos - a[home[x]].pos));
if (tmp == s) {
printf("Yes\n");
printf("%d %d\n", a[home[x]].pos.x, a[home[x]].pos.y);
printf("%d %d\n", a[home[y]].pos.x, a[home[y]].pos.y);
printf("%d %d\n", a[mid].pos.x, a[mid].pos.y);
exit(0);
}
if (tmp < s) l = mid + 1;
else r = mid - 1;
}
}
void Swap(int x, int y) {
if (home[x] != home[y] - 1) {
printf("Wrong!\n");
exit(0);
}
swap(home[x], home[y]);
swap(a[home[x]], a[home[y]]);
}
bool cmp(posi a, posi b) {
return a.pos < b.pos;
}
bool cnp(info a, info b) {
return a.angle < b.angle;
}
int main() {
read(n), read(s); s *= 2;
for (int i = 1; i <= n; i++)
read(a[i].pos.x), read(a[i].pos.y);
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++)
a[i].num = i, home[i] = i;
int tot = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (i != j) {
point tmp = a[j].pos - a[i].pos;
b[++tot] = (info) {atan2(tmp.y, tmp.x), i, j};
}
sort(b + 1, b + tot + 1, cnp);
for (int i = 1, tmp; i <= tot; i = tmp) {
tmp = i;
while (tmp <= tot && fabs(b[tmp].angle - b[i].angle) <= eps) tmp++;
for (int j = i; j <= tmp - 1; j++)
solve(b[j].y, b[j].x);
for (int j = i; j <= tmp - 1; j++)
Swap(b[j].y, b[j].x);
}
printf("No\n");
return 0;
}
**【E】**Raining season
【思路要点】
- 将原树改造为二叉树,进行边分治。
- 考虑过中心边的路径,我们将从中心边出发的每一条路径 描述为点 ,那么这个点集的上凸壳就能够用来描述从中心边出发的路径所有时刻的最优长度, 时刻的最优长度在斜率为 的直线与凸壳的切点处取到。
- 过中心边路径的凸壳即为两个从中心边出发的路径的凸壳的闵可夫斯基和。
- 将每一条中心边的凸壳上的点放在一起,再求一次凸壳,即为所有路径的凸壳。
- 分别计算每个时刻的最优长度即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
const long double one = 1.0;
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 {long long x, y; };
struct edge {int dest; point val; };
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, long long b) {return (point) {a.x * b, a.y * b}; }
long double operator * (point a, point b) {return one * a.x * b.y - one * a.y * b.x; }
vector <edge> a[MAXN], b[MAXN];
vector <point> ans, tmpa, tmpb, res;
int n, m, tot, rootx, rooty, cntcol, weight[MAXN], size[MAXN], col[MAXN];
void rebuild(int pos, int fa) {
for (unsigned i = 0; i < b[pos].size(); i++)
if (b[pos][i].dest == fa) {
b[pos].erase(b[pos].begin() + i);
break;
}
for (unsigned i = 0; i < b[pos].size(); i++)
rebuild(b[pos][i].dest, pos);
while (b[pos].size() > 2) {
tot++; unsigned sz = b[pos].size();
a[tot].push_back(b[pos][sz - 1]);
a[b[pos][sz - 1].dest].push_back((edge) {tot, b[pos][sz - 1].val});
a[tot].push_back(b[pos][sz - 2]);
a[b[pos][sz - 2].dest].push_back((edge) {tot, b[pos][sz - 2].val});
b[pos].resize(sz - 2);
b[pos].push_back((edge) {tot, (point) {0, 0}});
}
for (unsigned i = 0; i < b[pos].size(); i++) {
a[pos].push_back(b[pos][i]);
a[b[pos][i].dest].push_back((edge) {pos, b[pos][i].val});
}
}
void calsize(int pos, int fa, int curr) {
size[pos] = 1;
col[pos] = cntcol;
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i].dest != fa && col[a[pos][i].dest] == curr) {
calsize(a[pos][i].dest, pos, curr);
size[pos] += size[a[pos][i].dest];
}
}
void getroot(int pos, int fa, int curr, int tot) {
size[pos] = 1;
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i].dest != fa && col[a[pos][i].dest] == curr) {
getroot(a[pos][i].dest, pos, curr, tot);
size[pos] += size[a[pos][i].dest];
}
weight[pos] = max(size[pos], tot - size[pos]);
if (weight[pos] < weight[rootx]) rootx = pos, rooty = fa;
}
bool cmp(point a, point b) {
if (a.x == b.x) return a.y > b.y;
else return a.x < b.x;
}
void convexhull(vector <point> &a) {
sort(a.begin(), a.end(), cmp);
int top = 1;
for (unsigned i = 1; i < a.size(); i++) {
if (a[i].x == a[top - 1].x) continue;
while (top >= 2 && (a[top - 1] - a[top - 2]) * (a[i] - a[top - 2]) >= 0) top--;
a[top++] = a[i];
}
a.resize(top);
}
void calcsum(vector <point> &a, vector <point> &b, vector <point> &res) {
int posa = 1, posb = 1;
res.clear(), res.push_back(a[0] + b[0]);
while (posa < a.size() || posb < b.size()) {
if (posa == a.size()) res.push_back(res[res.size() - 1] + b[posb] - b[posb - 1]), posb++;
else if (posb == b.size()) res.push_back(res[res.size() - 1] + a[posa] - a[posa - 1]), posa++;
else if ((a[posa] - a[posa - 1]) * (b[posb] - b[posb - 1]) >= 0) res.push_back(res[res.size() - 1] + b[posb] - b[posb - 1]), posb++;
else res.push_back(res[res.size() - 1] + a[posa] - a[posa - 1]), posa++;
}
}
void dfs(int pos, int fa, int curr, point sum, vector <point> &tmp) {
tmp.push_back(sum);
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i].dest != fa && col[a[pos][i].dest] == curr)
dfs(a[pos][i].dest, pos, curr, sum + a[pos][i].val, tmp);
}
void work(int pos, int tot, int curr) {
if (tot == 1) return;
weight[rootx = 0] = tot + 1;
getroot(pos, 0, curr, tot);
cntcol++; calsize(rootx, rooty, col[rootx]);
cntcol++; calsize(rooty, rootx, col[rooty]);
tmpa.clear(), dfs(rootx, 0, col[rootx], (point) {0, 0}, tmpa), convexhull(tmpa);
tmpb.clear(), dfs(rootx, 0, col[rooty], (point) {0, 0}, tmpb), convexhull(tmpb);
calcsum(tmpa, tmpb, res);
for (unsigned i = 0; i < res.size(); i++)
ans.push_back(res[i]);
int tmpx = rootx, tmpy = rooty;
work(tmpx, size[tmpx], col[tmpx]);
work(tmpy, size[tmpy], col[tmpy]);
}
int main() {
read(n), read(m);
for (int i = 1; i <= n - 1; i++) {
int x, y, k, a;
read(x), read(y), read(k), read(a);
b[x].push_back((edge) {y, (point) {k, a}});
b[y].push_back((edge) {x, (point) {k, a}});
}
tot = n; rebuild(1, 0);
work(1, tot, 0);
convexhull(ans);
int pos = 0;
for (int i = 0; i <= m - 1; i++) {
long long now = ans[pos].x * i + ans[pos].y, nxt;
if (pos < ans.size() - 1) {
nxt = ans[pos + 1].x * i + ans[pos + 1].y;
while (pos < ans.size() - 1 && nxt >= now) {
pos++; now = nxt;
if (pos < ans.size() - 1) nxt = ans[pos + 1].x * i + ans[pos + 1].y;
}
}
printf("%lld ", now);
}
return 0;
}