封面图源 【动漫作业本】:https://space.bilibili.com/488779255
[竞赛地址] https://ac.nowcoder.com/acm/contest/82345
G. Concert
给定n个城市,m条边。每个城市有ai的价值
从城市1出发,走k步后回到城市1。
问最多可以收集到多少价值。
数据范围
1≤n,m,k≤5000
1≤ai≤10^9
H. Magic Transport
给定长度为n的数组a和m,
在限制一定要取第i个数的情况下,前i个数最多可以取多少个,使得其总和不超过m。
输出n个数。
有t组查询。
数据范围
T≤10
1≤N≤10^5
1≤M≤10^9
1≤ai≤M
G. Concert(暴力/图论/动态规划)
因为范围只有5000,我们可以模拟走k轮,每一轮维护每个节点能收集到的最大价值。
可以用滚动数组优化下空间
const int maxn = 5002;
#define ll long long
#define inf 1e18
int n, m, k;
int u, v;
int a[maxn];
vector<int> ve[maxn];
ll dp[2][maxn];
void solve() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
while (m--) {
scanf("%d%d", &u, &v);
ve[u].push_back(v);
ve[v].push_back(u);
}
int cur = 1, pre = 0;
for (int i = 2; i <= n; ++i) {
dp[pre][i] = -inf;
}
dp[pre][1] = a[1];
--k; // 剩下只需k - 1轮
while (k--) {
for (int i = 1; i <= n; ++i) {
dp[cur][i] = -inf;
}
for (int i = 1; i <= n; ++i) {
for (auto v: ve[i]) {
dp[cur][v] = max(dp[cur][v], dp[pre][i] + a[v]);
}
}
swap(cur, pre);
}
printf("%lld\n", dp[pre][1] < 0 ? -1 : dp[pre][1]);
}
H. Magic Transport(二分/树状数组)
思路很好想到。
就是在取了第i个数ai后,对于剩下的i-1个数,贪心地取最小的元素即可。
从i-1个数取前k个最小值,容易想到二分。
从乱序的数组中找到前k小元素的总和,可以用 线段树/树状数组+map实现。
先算出每个元素的排位,维护每个元素在 线段树/树状数组 对应位置的 价值和/个数和。
详见代码
const int maxn = 100002;
#define ll long long
#define inf 1e18
int n, m, k;
int a[maxn], num[maxn];
struct Tree {
ll sum[maxn];
int n;
void init(int sz) {
n = sz;
memset(sum, 0, sizeof(sum));
}
void change(int l, int r, ll val) {
update(l, val);
update(r + 1, -val);
}
ll Que(int l) {
return get_sum(l);
}
int lowbit(int x) {
return x & (-x);
}
ll get_sum(int x) {
ll res = 0;
for(; x > 0; x -= lowbit(x)) {
res += sum[x];
}
return res;
}
void update(int x, ll val) {
for(; x <= n; x += lowbit(x)) {
sum[x] += val;
}
}
}tr, tr2;
void solve() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
num[i] = a[i];
}
sort(num + 1, num + n + 1);
int idx = 0;
map<int, int> mp;
for (int i = 1; i <= n; ++i) {
mp[num[i]] = ++idx;
}
// for (int i = 1; i <= n; ++i) {
// printf("%d(%d) ", a[i], mp[a[i]]);
// }
// printf("\n");
tr.init(n + 1);
tr2.init(n + 1);
for (int i = 1; i <= n; ++i) {
int l = 0, r = n, mid;
while (l < r) {
mid = (l + r + 1) / 2;
if (tr.Que(mid) + a[i] > m) {
r = mid - 1;
} else {
l = mid;
}
}
// printf("(%d)%lld ", l, i - tr2.Que(l) - 1);
printf("%lld ", i - tr2.Que(l) - 1);
tr.update(mp[a[i]], a[i]);
tr2.update(mp[a[i]], 1);
--mp[a[i]]; // 这里是为了处理 重复数字的情况
}
printf("\n");
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。