180119十二月赛(巧妙并查集+巧妙贪心)

本文介绍如何使用并查集解决区间查询问题及利用贪心策略优化物品收集问题。通过具体实例演示了算法的设计思路与实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A(巧妙使用并查集)

题意:
共1e6个位置,q次操作,每次让第 i 个位置坐人或者查询 [l,r] 区间的最小编号的人
(1<=i, q<=1e6)

题解:
如果把每个有人的位置指向自己,把没有人的位置指向下一个位置,那么每次查询相当于是查询一次l的根节点。所以把询问离线存下来维护一个并查集即可。注意每次查询需要在O(1)的时间内才可以。


使用FastIO的输入挂后,cout,printf等输出函数可能不会将输出显示到控制台中,调试过后直接交就可以。
表示原本2.1s超时,把输出的cout改成printf后,瞬间变成0.5s

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int fa[maxn];
int ans[maxn];
namespace fastIO {

    #define BUF_SIZE 100000

    bool IOerror = 0;

    inline char nc() {

        static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;

        if(p1 == pend) {

            p1 = buf;

            pend = buf + fread(buf, 1, BUF_SIZE, stdin);

            if(pend == p1) {

                IOerror = 1;

                return -1;

            }

        }

        return *p1++;

    }

    inline bool blank(char ch) {

        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';

    }

    inline void read(int &x) {

        char ch;

        while(blank(ch = nc()));

        if(IOerror)

            return;

        for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');

    }

    #undef BUF_SIZE


};

int find_fa(int x){
    if(x == fa[x]) return x;
    return fa[x] = find_fa(fa[x]);
}

struct operate{
    int x, y, z;
}op[maxn];

int main()
{
    int T; fastIO::read(T);
    int cas = 1;
    int cnt = 0;
    while(T--){
        int opn;
        fastIO::read(opn);
        //cin >> opn;
        cnt = 0;
        for(int i = 0; i < maxn; i++) fa[i] = i+1;
        fa[maxn-1] = maxn-1;
        for(int i = 0; i < opn; i++){
            //cin >> op[i].x >> op[i].y;
            fastIO::read(op[i].x);
            fastIO::read(op[i].y);
            if(op[i].x == 2)
                fastIO::read(op[i].z);
                //cin >> op[i].z;
            else fa[op[i].y] = op[i].y;
        }

        while(opn--){
            if(op[opn].x == 1){
                int y = op[opn].y;
                fa[y] = y+1;
            }
            else{
                int f = find_fa(op[opn].y);
                if(f > op[opn].z) ans[++cnt] = -1;
                else ans[++cnt] = f;
            }
        }
        printf("Case #%d:\n", cas++);
        while(cnt)
            printf("%d\n", ans[cnt--]);
            //cout << ans[cnt--] << endl;
    }
    return 0;
}

J

题意:
已知n个城市的位置,有个毒圈从1号城市开始缩,要求人在某一个时刻前跑进某一个城市。每个城市有一个物品,捡起它需要花费一定时间,求出从1安全抵达n最多能捡多少物品。

题解
非常有趣的水题。非常显然的贪心是能捡就捡,但是这样存在的问题是可能捡了一个时间花费多的导致后面时间花费少的不能捡了。所以用优先队列维护捡
了的物品,每次到一个城市都把里面的物品捡起来,然后如果不能安全到达下一个城市的话就把队列中花费最久的“反悔”掉,这样可以保证是最优的并且是安全的。时间复杂度O(nlgn)

待查错

#include <bits/stdc++.h>
using namespace std;
int n;
const int maxn = 2e5+10;
int l[maxn], t[maxn], p[maxn];

int main()
{
    int T; cin >> T;
    int cas = 1;
    while(T--){
        priority_queue<int, vector<int>, greater<int> > que;
        while(!que.empty()) que.pop();
        cin >> n;
        for(int i = 0; i < n; i++) scanf("%d", &l[i]);
        for(int i = 0; i < n; i++) scanf("%d", &t[i]);
        for(int i = 0; i < n-1; i++) scanf("%d", &p[i]);
        int cur = 0;
        for(int i = 0; i < n-1; i++){
            cur += p[i]; que.push(p[i]);
            while(cur+l[i+1]-l[i]>t[i+1]){
                int q = que.top(); 
                cur-=q; que.pop();
            }
            cur += l[i+1]-l[i];
        }
        printf("Case #%d: %d\n", cas++, que.size());
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值