2022“杭电杯”中国大学生算法设计超级联赛(5)
[题目链接](Search Result (hdu.edu.cn))
L Buy Figurines
题目大意
有n个人买东西,第i个人会在ai时刻来排队,花费si的时间。现有m个队伍,每个人会选择当前时刻中,排队人数最少的队伍,如果有多个最少的,选择编号最小的队伍。问最后一个人离开的时间。
题解
线段树,叶子节点表示队伍。
查找某个人排哪个队伍:如果左边的结点的最小值小于等于右边的,就去左边,否则就去右边。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
int t, n, m;
ll last[maxn];
struct Node
{
int a, s;
bool operator<(const Node &b) const
{
return a < b.a;
}
} peo[maxn];
struct Node2
{
ll end;
int id;
bool operator<(const Node2 &b) const
{
return end > b.end;
}
};
struct Tree
{
int l, r, num;
} tree[maxn << 2];
void PushUp(int rt)
{
tree[rt].num = min(tree[rt << 1].num, tree[rt << 1 | 1].num);
}
void Build(int l, int r, int rt)
{
tree[rt].l = l;
tree[rt].r = r;
tree[rt].num = 0;
if (l == r)
return;
int m = (l + r) >> 1;
Build(l, m, rt << 1);
Build(m + 1, r, rt << 1 | 1);
PushUp(rt);
}
void Update(int rt, int id, int x)
{
if (tree[rt].l == tree[rt].r)
{
tree[rt].num += x;
return;
}
int m = (tree[rt].l + tree[rt].r) >> 1;
if (id <= m)
Update(rt << 1, id, x);
else
Update(rt << 1 | 1, id, x);
PushUp(rt);
}
int Query(int rt)
{
if (tree[rt].l == tree[rt].r)
return tree[rt].l;
if (tree[rt << 1].num <= tree[rt << 1 | 1].num)
return Query(rt << 1);
else
return Query(rt << 1 | 1);
}
int Get(int rt, int id)
{
if (tree[rt].l == tree[rt].r)
return tree[rt].num;
int m = (tree[rt].l + tree[rt].r) >> 1;
if (id <= m)
return Get(rt << 1, id);
else
return Get(rt << 1 | 1, id);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--)
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> peo[i].a >> peo[i].s;
for (int i = 1; i <= m; i++)
last[i] = 0;
sort(peo + 1, peo + 1 + n);
Build(1, m, 1);
priority_queue<Node2> q;
for (int i = 1; i <= n; i++)
{
while (q.size() && q.top().end <= peo[i].a)
{
int id = q.top().id;
Update(1, id, -1);
q.pop();
}
int id = Query(1);
if (Get(1, id) == 0)
last[id] = peo[i].a + peo[i].s;
else
last[id] += peo[i].s;
q.push({last[id], id});
Update(1, id, 1);
}
cout << *max_element(last + 1, last + 1 + m) << endl;
}
return 0;
}
J Bragging Dice
题目大意
两个人玩游戏,每个人有n个骰子,每个人只能摇一次。然后有两个选择,可以说一个现在2n个骰子的情况,如有4个1点;或挑战对方,若对方说对了,则对方赢,否则,挑战方赢。
特殊规则:
1、若此前未有人说过1点,则1点可以被当成任意点数。
2、若n个骰子都是一样的点数,则有n+1个该点数。
3、若n个骰子点数各不相同,则每个点数的个数都为0。
题解
当双方都摇出全部不同的点数时,此时先手无论如何说都是会被挑战成功的。除此之外的情况,先手都能说出最大的x个y,此时后手没有正确的情况可以说,一定会被先手挑战成功。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
int t, n, x;
int a[7], b[7];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--)
{
cin >> n;
memset(a, 0, sizeof a);
memset(b, 0, sizeof b);
for (int i = 1; i <= n; i++)
{
cin >> x;
a[x]++;
}
for (int i = 1; i <= n; i++)
{
cin >> x;
b[x]++;
}
bool ok1 = 1, ok2 = 1;
for (int i = 1; i <= 6; i++)
if (a[i] > 1)
ok1 = 0;
for (int i = 1; i <= 6; i++)
if (b[i] > 1)
ok2 = 0;
if (ok1 && ok2)
cout << "Just a game of chance." << endl;
else
cout << "Win!" << endl;
}
return 0;
}
C Slipper
题目大意
给定一棵以1为根的树,每条边都有边权w,经过一条边的花费为边的边权。同时对于两个节点u,v,如果两点深度之差为k,那么只需要花费p就可以从u到v,或者从v到u。求从点s到点t的最小花费。
题解
最短路dijkstra。
本题难点在于深度之差为k的点之间的连边。在第i层和第i+k层之间增加两个点:其中一个点为所有第i层的点到该点,该点到所有的i+k层;另一个点为所有第i+k层的点到该点,该点到所有的i层。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const int maxn = 3e6 + 5;
const int maxm = 8e6 + 5;
int T, n, k, p, s, t;
ll dis[maxn];
int head[maxn], cnt, maxdeep;
vector<int> v[maxn];
struct Edge
{
int to, w, next;
} edge[maxm];
void init()
{
memset(head, -1, sizeof head);
maxdeep = cnt = 0;
for (int i = 0; i < maxn; i++)
v[i].clear();
return;
}
void add(int u, int v, int w)
{
edge[cnt].w = w;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void dij()
{
memset(dis, 0x3f3f3f3f, sizeof dis);
priority_queue<P, vector<P>, greater<P>> q;
dis[s] = 0;
q.push(P(0, s));
while (!q.empty())
{
P p = q.top();
q.pop();
int u = p.second;
if (u == t)
return;
if (dis[u] < p.first)
continue;
for (int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to, w = edge[i].w;
if (dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
q.push(P(dis[v], v));
}
}
}
}
void dfs(int u, int fa, int deep)
{
v[deep].push_back(u);
maxdeep = max(maxdeep, deep);
for (int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
if (v != fa)
dfs(v, u, deep + 1);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> T;
while (T--)
{
init();
cin >> n;
for (int i = 1; i < n; i++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
cin >> k >> p >> s >> t;
dfs(1, 0, 0);
for (int i = 0; i <= maxdeep; i++)
{
if (i + k > maxdeep)
break;
n++;
for (auto e : v[i])
add(n, e, p);
for (auto e : v[i + k])
add(e, n, 0);
n++;
for (auto e : v[i])
add(e, n, p);
for (auto e : v[i + k])
add(n, e, 0);
}
dij();
cout << dis[t] << endl;
}
return 0;
}