我解决的:K、B(得知算法后写出)、J(2 WA)。
没看的:D(不会做)。
我旁观的:A、C、H、I、G、E(我可能能做出来,但是估计要写一会)。
看了不会做的:F。
K Kleptography
简单题,略。我还想了一会才写出来,代码能力属实不行。
I Inflation
简单题,略。
H Hard Drive
应该是简单题?略。
J Jinxed Betting
题意(转化后):给定一些数,每次可以对这些数进行操作,一次操作为:设最大的数有 个,将最大的 个数,以及其他所有非最大的数均加 1。再给定一个目标 ,问至多操作几次,可以保证这些数中的最大者不超过 。
手算一下可以发现,如果某一时刻最大的数 有 个,第二大的数 有 个,那么 轮后最大的数变成了 个,即原本第二大的数全部长成了最大。
于是我们可以模拟这种合并的过程,通过一些简单的计算就可以得到答案。时间复杂度为 ,因为要排序。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll targ, a[100005];
stack<pair<ll, int> > st;
int logg[100005];
int main(){
scanf("%d%lld", &n, &targ);
--n;
for (int i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
sort(a + 1, a + n + 1);
for (int i = 1, j; i <= n; i = j){
j = i;
while (j <= n && a[i] == a[j])
++j;
st.push(make_pair(a[i], j - i));
}
logg[1] = 1;
for (int i = 2; i <= n; ++i)
logg[i] = logg[i >> 1] + 1;
ll tot_round = 0;
// 由于不能批量加法,因此认为每一个数的实际值要加上 tot_round
while (st.size() > 1){
ll ori_x1 = st.top().first + tot_round, x1 = ori_x1;
int a1 = st.top().second;
st.pop();
ll x2 = st.top().first + tot_round;
int a2 = st.top().second;
st.pop();
ll round_needed = (x1 - x2) * logg[a1];
ll x1_added = round_needed - (x1 - x2);
x1 += x1_added;
if (x1 > targ){
// 在这里停下,结算
ll round_before = ((targ - ori_x1) / (logg[a1] - 1)) * logg[a1];
ll ori_x1_added = ((targ - ori_x1) / (logg[a1] - 1)) * (logg[a1] - 1);
printf("%lld\n", tot_round + round_before +
(targ - ori_x1 - ori_x1_added));
return 0;
} else {
tot_round += round_needed;
x1 -= tot_round;
st.push(make_pair(x1, a1 + a2));
}
}
ll x1 = st.top().first + tot_round;
ll round_before = ((targ - x1) / (logg[n] - 1)) * logg[n];
ll x1_added = ((targ - x1) / (logg[n] - 1)) * (logg[n] - 1);
printf("%lld\n", tot_round + round_before +
(targ - x1 - x1_added));
return 0;
}
B Brexit Negotiations
题意:给一个 DAG,最小化:每个点点权与其在拓扑序内的编号之和的最大值。
反向建图,然后拓扑排序即可。用一个堆代替队列,每次取出点权最小的点进行扩展。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, d[400005], du[400005] = {0};
vector<int> G[400005];
priority_queue<pair<int, int>, vector<pair<int, int> >,
greater<pair<int, int> > > pq;
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; ++i){
int t, to;
scanf("%d%d", &d[i], &t);
for (int j = 1; j <= t; ++j){
scanf("%d", &to);
G[i].push_back(to);
++du[to];
}
}
for (int i = 1; i <= n; ++i){
if (!du[i]) pq.push(make_pair(d[i], i));
}
int ans = 0, tot = n;
while (!pq.empty()){
int h = pq.top().second;
pq.pop();
--tot;
ans = max(ans, d[h] + tot);
for (int v: G[h]){
--du[v];
if (!du[v]) pq.push(make_pair(d[v], v));
}
}
printf("%d\n", ans);
return 0;
}
E Equality Control
题意:给定两个经某些操作得到的序列,操作有拼接、shuffle、排序等。问最后得到的两个序列是否有相同的概率分布。
写一个 parser,然后比对两个序列是否相同即可。发现有三点:
- 如果是 shuffle 或者排序,那么这两个操作造成的影响不会交叉,且外层的影响会覆盖内层的影响。例如 shuffle 一个部分排序的序列,内部的排序就没有任何用处了。
- shuffle 只含一种元素的序列不会有任何影响,可以认为没有这个操作。
- shuffle 后的分布相同当且仅当 shuffle 的对象完全相同。
#include <bits/stdc++.h>
#define MOD 1000000007
using namespace std;
inline int read(int& l, char *s){
int x = 0;
char c = s[l];
while (c >= '0' && c <= '9')
x = x * 10 + c - '0', c = s[++l];
return x;
}
char s1[1000005], s2[1000005];
int l1, l2;
int lis1[1000005], lis2[1000005], tot1, tot2;
int r1[1000005][2] = {0}, r2[1000005][2] = {0};
int ps(int l, int tp, int& tot, char s[], int lis[], int r[][2]){
if (s[l] == '['){
// atomic
++l;
for (; ; ){
lis[++tot] = read(l, s);
if (s[l] == ']') break;
else ++l;
}
return l;
}else if (s[l] == 's'){
// sorted or shuffle
int old_tot = tot;
int new_tp = (s[l + 1] == 'h' ? 2: 1);
int rp = ps(l + 6 + new_tp, tp == 0 ? new_tp : tp, tot, s, lis, r);
// r: 操作作用域,于左端点记录该操作的右端点
if (tp == 0){
r[old_tot + 1][0] = tot;
r[old_tot + 1][1] = new_tp;
}
return rp + 1;
}else{
// concat
int rp = ps(l + 7, tp, tot, s, lis, r);
int rp2 = ps(rp + 2, tp, tot, s, lis, r);
return rp2 + 1;
}
return 0;
}
void deal(int tot, int lis[], int r[][2]){
for (int i = 1; i <= tot; ++i){
if (r[i][1] != 0){
sort(lis + i, lis + r[i][0] + 1);
if (r[i][1] == 1) r[i][0] = r[i][1] = 0;
else {
bool flag = false;
for (int j = i + 1; j <= r[i][0]; ++j)
if (lis[j] != lis[j - 1]){
flag = true;
break;
}
if (!flag) r[i][0] = r[i][1] = 0;
}
}
}
}
void init(){
scanf("%s%s", s1, s2);
l1 = strlen(s1), l2 = strlen(s2);
tot1 = tot2 = 0;
ps(0, 0, tot1, s1, lis1, r1), deal(tot1, lis1, r1);
ps(0, 0, tot2, s2, lis2, r2), deal(tot2, lis2, r2);
}
void solve(){
if (tot1 != tot2){
printf("not equal\n");
return ;
}
for (int i = 1; i <= tot1; ++i){
if (r1[i][1] != 0 || r2[i][1] != 0){
// shuffle?
if (r1[i][1] == 0 || r2[i][1] == 0 || r1[i][0] != r2[i][0]){
printf("not equal\n");
return ;
}
int rb = r1[i][0];
while (i <= rb){
if (lis1[i] != lis2[i]){
printf("not equal\n");
return ;
}
++i;
}
--i;
}else {
if (lis1[i] != lis2[i]){
printf("not equal\n");
return ;
}
}
}
printf("equal\n");
}
int main(){
init();
solve();
return 0;
}
C Circuit Design
题意:给定一个树,将其画在二维平面上,要求边不能交叉,边的欧几里得长度固定为 1,点之间不能离得太近。
直接从原点向外面扩展就行了,每一个子树按照子树大小划分角度范围即可。由于只有 1000 个点,因此点之间的距离可以得到保证。
A Access Points
题意:给定平面上 个点 ,找到 个点 ,使得对于任意 , 的横坐标不大于 的横坐标、 的纵坐标不大于 的纵坐标,且最小化 。
可以单独对横、纵坐标分别求解,然后把答案加起来。这样就转化为了一维的情况。
一维的问题是:给定 ,找出 ,最小化 。
会发现:当 全部取相同值时, 取 的均值最优。然后结论就是:最优的 是成段的,每一段中 的值都是 中同位置一段的平均值。
说实话,这个结论我是不知道怎么推出来的。
这个可以用一个单调栈维护。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, x[100005], y[100005];
ll sum[100005];
int cnt[100005], top;
inline double sqr(double xx){
return xx * xx;
}
double calc(int a[]){
top = 0;
for (int i = 1; i <= n; ++i){
int cur_cnt = 1;
ll cur_sum = a[i];
while (top > 0 && sum[top] * cur_cnt > cur_sum * cnt[top]){
cur_cnt += cnt[top];
cur_sum += sum[top];
--top;
}
sum[++top] = cur_sum, cnt[top] = cur_cnt;
}
double res = 0;
for (int t = 1, i = 1; t <= top; ++t){
double val = sum[t] / (1.0 * cnt[t]);
for (int j = 1; j <= cnt[t]; ++j, ++i)
res += sqr(val - a[i]);
}
return res;
}
void init(){
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d", &x[i], &y[i]);
}
void solve(){
double res_x = calc(x);
double res_y = calc(y);
printf("%.20lf\n", res_x + res_y);
}
int main(){
init();
solve();
return 0;
}
G Game Design
题意:给定一个小球的移动序列,需要构造一个迷宫的方案,使得小球按照你自行指定的起点按照移动序列移动后到达 。
首先要发现:无解的充要条件是末尾三个移动是 LRL
,UDU
这种形式。
然后从前往后构造,每次让迷宫的边界扩大一定的大小(如 2)。如果一次向一个方向移动后又要向反方向移动就不扩大迷宫。最后将到达点和所有障碍物平移,使得到达点变成 即可。
std 在移动方向的两侧也构建了障碍物,我觉得这个做法没有上面的好。
本题从后往前构造似乎会比较麻烦。
#include <bits/stdc++.h>
using namespace std;
char s[25];
vector<pair<int, int> > ans;
int mp[300] = {0};
int main(){
scanf("%s", s);
int l = strlen(s);
mp['L'] = mp['R'] = 0;
mp['U'] = mp['D'] = 1;
if (l >= 3 && s[l - 1] == s[l - 3] && mp[s[l - 1]] == mp[s[l - 2]]){
printf("impossible\n");
return 0;
}
int bd = 3, x = 0, y = 0;
for (int i = 0; i < l; ++i){
if (s[i] == 'L') x = -bd, ans.emplace_back(x - 1, y);
if (s[i] == 'R') x = bd, ans.emplace_back(x + 1, y);
if (s[i] == 'U') y = bd, ans.emplace_back(x, y + 1);
if (s[i] == 'D') y = -bd, ans.emplace_back(x, y - 1);
if (i + 1 < l && mp[s[i]] != mp[s[i + 1]])
bd += 2;
}
sort(ans.begin(), ans.end());
printf("%d %d\n", -x, -y);
int n = unique(ans.begin(), ans.end()) - ans.begin();
printf("%d\n", n);
for (int i = 0; i < n; ++i)
printf("%d %d\n", ans[i].first - x, ans[i].second - y);
return 0;
}
F Fastest Speedrun
待补。。。
D Date Pickup
待补。。。