头图

封面图源 【动漫作业本】: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");
    
}

对方正在debug
1 声望0 粉丝