cf round #536(div 2) (未完)

博主分享了参加2019年1月31日的CF比赛经历,因身体不适影响比赛表现,但幸运未掉分。解析了B题模拟问题,使用优先队列解决;C题为数据分组问题,提出最优解策略;D题为图遍历问题,给出字典序最小路径算法。分享了E题红包抢夺策略与F题未解之谜。

2019/1/31
div2 AC 4/6
unrated 差点掉分

八点半的cf,然而可能是前一天太晚睡了,也可能是下午刚二刷了白蛇消耗了太多精力,头疼的不行,比赛前床上小睡了一会。比赛状态果然不行,不过幸好因为long queue unrated了,然后就干别的事情去了。
比赛难度设置也有点问题,B的模拟难度应该有C到D的水准,而CD又比较简单,合适的难度应该是CDB这样。

B
模拟题,敲了快半个小时吧,其实也不是很难敲,大概是太累了。
用一个优先队列维护就可以了。
交了之后一直in queue, 然后做C的时候突然意识到忘了开long long, 就define int ll。结果居然编译不通过,傻逼一样查了半天错发现ll后面多了一个分号。所以果然是太累了吧……

#include<bits/stdc++.h>
#define forn(i, n) for (int i = 0; i < (n); i++)
#define forab(i, a, b) for (int i = (a); i <= (b); i++)
#define forba(i, b, a) for (int i = (b); i >= (a); i--)
#define mset(a, n) memset(a, n, sizeof(a))
#define endl '\n'
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;

const int maxn = 1e5 + 5;

struct node{
    int cost, rest, id;
    bool operator < (const node &a) const {
        if (cost != a.cost) return cost > a.cost;
        return id < a.id;
    }
};

node a[maxn];
int l[maxn];

int main()
{
    int n, m;
    priority_queue<node> q;
    fast;
    cin >> n >> m;
    forn(i, n)
    {
        cin >> a[i].rest;
        l[i] = a[i].rest;
    }
    forn(i, n)
    {
        cin >> a[i].cost;
        a[i].id = i;
        q.push(a[i]);
    }
    while (m--)
    {
        int t, num;
        int sum = 0;
        cin >> t >> num;
        t--;
        if (l[t] < num) 
        {
            sum += a[t].cost * l[t];
            num -= l[t];
            l[t] = 0;
            while (!q.empty() && num)
            {
                node b = q.top();
                q.pop();
                b.rest = l[b.id];
                int temp = min(b.rest, num);
                sum += b.cost * temp;
                num -= temp;
                l[b.id] -= temp;
                if (l[b.id]) {
                    b.rest -= temp;
                    q.push(b);
                }
            }
            if (q.empty() && num)
                sum = 0;
        }
        else {
            l[t] -= num;
            sum += a[t].cost * num;
        }
        cout << sum << endl;
    }
}

C
偶数个数字,要你分成若干组,每组至少两个数字,使得每组各自求和后,所有和的平方和最小。
结论要猜很容易,证明也不难 = =最大的和最小的,次大的和次小的以此类推即可.
代码也只要排序+循环,完全不符合C题难度。


D
给你一张连通的无向图(可能有自环和重边)。现在从点1出发访问各点(点和边可以重复访问),每次到达一个没有被访问过的点后记录下该点的序号,直到所有点都被访问过至少一次后停止。显然最后得到的是1到n的一个排列,请你求出所有走法中字典序最小的排列。
一开始没想明白卡了一会,其实只要每次访问一个点之后把没访问过的相邻点加入优先队列即可。

#include<bits/stdc++.h>
#define forn(i, n) for (int i = 0; i < (n); i++)
#define forab(i, a, b) for (int i = (a); i <= (b); i++)
#define forba(i, b, a) for (int i = (b); i >= (a); i--)
#define mset(a, n) memset(a, n, sizeof(a))
#define endl '\n'
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;

const int maxn = 1e5 + 5;

int n, m;
vector<int> g[maxn];
int vis[maxn];
int ans[maxn];
int cur = 0;

priority_queue<int, vector<int>, greater<int> > q;

int main()
{
    fast;
    cin >> n >> m;
    forn(i, m)
    {
        int x, y;
        cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    q.push(1);
    vis[1] = 1;
    while (!q.empty())
    {
        int t = q.top();
        q.pop();
        ans[cur++] = t;
        forn(i, (int) g[t].size())
        {
            if (!vis[g[t][i]]) {
                vis[g[t][i]] = 1;
                q.push(g[t][i]);
            }
        }
    }
    forn(i, n)
        cout << ans[i] << ' ';
}

E
Bob要抢红包,红包有四个属性,开始时间s和结束时间t,抢了红包后直到包括d之前的时间都不能抢,红包里有w元。Bob每次在会选择当前时间金额最大的,相同选d最大的,再相同就随便选一个。Alice可以干扰Bob m次,每次使Bob在当前时间没办法抢红包。求Alice采用最优策略下Bob拿到的最少钱数。
题意比较绕,当时一眼没看明白,然后因为是unrated就没接着看了。第二天看了一下果然不会做。
看了题解,官方代码写的挺迷的,只借鉴了思路。先用multiset或者map维护红包。时间从1到n扫描一遍,每次加入该时刻开始的红包,删掉结束的红包。以此求出t时刻能取得的最大价值以及跳转到的时间。
然后dp, dp[i][j] 代表前i时刻干扰j次取得的最小钱数。

#include<bits/stdc++.h>
#define forn(i, n) for (int i = 0; i < (n); i++)
#define forab(i, a, b) for (int i = (a); i <= (b); i++)
#define forba(i, b, a) for (int i = (b); i >= (a); i--)
#define mset(a, n) memset(a, n, sizeof(a))
#define endl '\n'
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;

const int maxn = (int) 1e5 + 5;
const int maxm = 200 + 5;

struct node {
    int d, w;
    bool operator < (const node &a) const {
        if (w != a.w) return w > a.w;
        return d > a.d;
    }
};
multiset<node> q;
ll dp[maxn][maxm];
int f[maxn];
int d[maxn];
vector<node> vi[maxn];
vector<node> vd[maxn];

int main()
{
    int n, m, k;
    fast;
    cin >> n >> m >> k;
    forn(i, k)
    {
        int s, t, d, w;
        cin >> s >> t >> d >> w;
        vi[s].push_back(node{d, w});
        vd[t + 1].push_back(node{d, w});
    }
    forn(t, n + 1)
        d[t] = t;
    forab(t, 1, n)
    {
        for (auto it : vi[t]) q.insert(it);
        for (auto it : vd[t]) q.erase(q.find(it));
        if (!q.empty()) {
            f[t] = q.begin()->w;
            d[t] = q.begin()->d;
        }
    }
    mset(dp, 0x3f);
    mset(dp[0], 0);
    forab(i, 1, n)
    {
        forab(j, 0, m)
        {
            dp[i][j + 1] = min(dp[i][j + 1], dp[i - 1][j]);
            dp[d[i]][j] = min(dp[d[i]][j], dp[i - 1][j] + f[i]);
        }
    }
    ll ans = 0x3f3f3f3f3f3f3f3f;
    forn(i, m + 1)
        ans = min(ans, dp[n][i]);
    cout << ans << endl;
}

F
数学啥都不会,学完再来补。

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值