zoukankan      html  css  js  c++  java
  • Gym102001

    题目

    Problem - H - Codeforces

    给定一个整数序列({a_i})满足(-1le a_ile 1),给定(k)个约束条件((l,r,d))代表(sumlimits_{i=l}^{r}{a_i}ge d)。你需要将({a_i})中所有为0的位置的值替换为-1或1,使得最终序列满足所有约束条件且字典序最小,无解输出impossible

    题解

    方法1

    先将所有为0的位置用-1填充,然后按照约束区间右端点从小到大排序。然后遍历约束区间从右到左将可修改位置贪心地将-1改为1,直到序列满足当前约束区间的条件。用数状数组维护区间和,set维护可修改位置,每次修改一个位置后将其从set中删去。

    正确性:为了字典序最小,一开始用-1填充,然后按从右到左的顺序将尽可能少的-1改为1使得满足约束。为了修改尽可能少的-1,就要选择约束区间重叠最多位置。按照约束区间右端点从小到大排序后,从右到左贪心选择修改位置,可以保证选择的位置重叠的区间最多。而且每个约束区间都要满足,这样贪心地选一定是最优的。

    #include <bits/stdc++.h>
    
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 3e5 + 10;
    const double eps = 1e-5;
    
    int tarr[N];
    int n, k;
    
    int lowbit(int x) {
        return x&-x;
    }
    
    void add(int p, int val) {
        while(p <= n) {
            tarr[p] += val;
            p += lowbit(p);
        }
    }
    
    int sum(int p) {
        int res = 0;
        while(p) {
            res += tarr[p];
            p -= lowbit(p);
        }
        return res;
    }
    
    set<int> pos;
    
    struct node {
        int l, r, d;
        bool operator<(const node& rhs) const {
            if(r == rhs.r) return l < rhs.l;
            return r < rhs.r;
        }
    };
    
    node seg[N];
    int arr[N];
    
    int main() {
        IOS;
        cin >> n >> k;
        for(int i = 1; i <= n; i++) {
            cin >> arr[i];
            if(arr[i] == 0) {
                pos.insert(i);
                arr[i] = -1;
            }
            add(i, arr[i]);
        }
        for(int i = 1; i <= k; i++) {
            int l, r, d;
            cin >> l >> r >> d;
            seg[i] = node{l, r, d};
        }
        sort(seg + 1, seg + 1 + k);
        bool ok = true;
        for(int i = 1; i <= k; i++) {
            int l = seg[i].l, r = seg[i].r, d = seg[i].d;
            d = sum(r) - sum(l - 1) - d;
            while(d < 0) {
                auto it = pos.upper_bound(r);
                if(it == pos.begin()) {
                    ok = false;
                    break;
                }
                it--;
                if(*it < l) {
                    ok = false;
                    break;
                }
                arr[*it] = 1;
                add(*it, 2);
                d += 2;
                pos.erase(it);
            }
        }
        if(!ok) cout << "Impossible" << endl;
        else {
            for(int i = 1; i <= n; i++) cout << arr[i] << " 
    "[i == n];
        }
    }
    

    方法2

    按照常规的思路想就是,一开始将0填充为1,然后从左往右遍历可修改位置,判断此时修改成-1是否还满足所有约束条件,如果满足就贪心地改成-1;否则遍历下一个可修改位置。这样的正确性是显然的,但暴力会超时。

    考虑优化。对于约束条件((l,r,d)),其等价形式就是区间((l,r))内最多有(lfloorfrac{r-l+1-d}{2} floor)个-1。

    因此可以维护一个数据结构,当遍历到位置(i),将左端点的为(i)的区间以其可用的-1数量插入数据结构,然后查询当前位置(i)如果变为-1,数据结构中最少的可用的-1数量是否大于0。如果发生修改,就对将数据结构中所有的可用的-1数量均减去1。最后再将右端点为(i)的区间全部弹出。然后处理下一个位置。

    显然,用这种方法,任意时刻到达位置(i),数据结构中就含有所有包含位置(i)的区间。如果要支持修改,会很难实现,所以可以反过来,所有减去1,相当于其他加上1。维护一个(cnt)代表当前修改了多少-1,然后插入(lfloorfrac{r-l+1-d}{2} floor+cnt),查询时和当前(cnt)比较即可。这样就只有插入查询和删除,可以用set实现。思想类似有一题聊天室的题,用差分代替每次修改。

    #include <bits/stdc++.h>
    
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 3e5 + 10;
    const double eps = 1e-5;
    
    
    
    struct node {
        int l, r, d;
    };
    typedef pair<int, int> PII; 
    node seg[N];
    int arr[N];
    vector<int> segl[N], segr[N];
    set<PII> segs;
    int limit[N];
    int pre[N];
    int precnt[N];
    
    bool check(int n, int k) {
        for(int i = 1; i <= n; i++) pre[i] = pre[i - 1] + arr[i];    
        for(int i = 1; i <= k; i++) {
            int l = seg[i].l, r = seg[i].r, d = seg[i].d;
            if(pre[r] - pre[l - 1] < d) return false;
        }
        return true;
    }
    
    int main() {
        IOS;
        int n, k;
        cin >> n >> k;
        for(int i = 1; i <= n; i++) {
            cin >> arr[i];
            if(arr[i] == -1) precnt[i] = 1;
            precnt[i] += precnt[i - 1];
        }
        for(int i = 1; i <= k; i++) {
            int l, r, d;
            cin >> l >> r >> d;
            seg[i] = node{l, r, d};
            segl[l].push_back(i);
            segr[r].push_back(i);
        }
        int cnt = 0;
        for(int i = 1; i <= n; i++) {
            for(auto id : segl[i]) {
                int l = seg[id].l, r = seg[id].r, d = seg[id].d;
                segs.insert({(r - l + 1 - d) / 2 + cnt - (precnt[r] - precnt[l - 1]), id});
                limit[id] = (r - l + 1 - d) / 2 + cnt - (precnt[r] - precnt[l - 1]); // 原先就有的-1要先减去
            }
            if(!arr[i]) {
                if(segs.empty() || segs.begin()->first >= cnt + 1) {
                    cnt++;
                    arr[i] = -1;
                } else {
                    arr[i] = 1;
                }
            }
            for(auto id : segr[i]) {
                segs.erase({limit[id], id});
            }
        }
        if(!check(n, k)) cout << "Impossible" << endl;
        else {
            for(int i = 1; i <= n; i++) cout << arr[i] << " 
    "[i == n];
        }
    }
    
  • 相关阅读:
    Centos下Oracle11gR2安装教程与自动化配置脚本
    你知道CPU结构也会影响Redis性能吗?
    谈谈InnoDB中的B+树索引
    理论:第十一章:大厂程序员如何使用GitHub快速开发学习
    理论:第十四章:生产环境服务器变慢如何诊断,性能评估
    理论:第十三章:堆溢出,栈溢出的出现场景以及解决方案
    理论:第十二章:Dubbo的运行原理,支持什么协议,与SpringCould相比它为什么效率要高一些,Zookeeper底层原理
    理论:第十章:公平锁,非公平锁,可重入锁,递归锁,自旋锁,读写锁,悲观锁,乐观锁,行锁,表锁,死锁,分布式锁,线程同步锁分别是什么?
    理论:第九章:JVM内存模型,算法,垃圾回收器,调优,四大引用,常见的JVM错误,类加载机制(双亲委派),创建一个对象,这个对象在内存中是怎么分配的?
    理论:第八章:线程是什么,有几种实现方式,它们之间的区别是什么,线程池实现原理,JUC并发包,ThreadLocal与Lock和Synchronize区别
  • 原文地址:https://www.cnblogs.com/limil/p/15187270.html
Copyright © 2011-2022 走看看