zoukankan      html  css  js  c++  java
  • 【2019 CCPC 哈尔滨站 】A. Artful Paintings 差分约束+二分

    题目链接

    题意

    给出一个长度为 n 的方块区间[1,n],现在要对这个方块区间染色,有两种要求:

    1. 格式:l, r, k。表示区间[l,r]里至少有 k 个方块被染色
    2. 格式:l, r, k。表示区间[l,r]之外至少有 k 个方块被染色。

    现在给出 n, m1, m2,分别表示有 n 个方块,m1 个 1 类型的要求,m2 个 2 类型的需求。
    问最少需要染多少个方块满足所有的要求。

    思路

    只看要求 1 的话,经典的差分约束。

    但是第二个如果列成不等式会有三个变量:(pre[n] - (pre[r]-pre[l-1]) geq k)

    化简一下:(pre[l-1] -pre[r] geq k-pre[n])

    可以看出 第二个式子中一直存在(pre[n]),那么就想到可不可以先把(pre[n])看做一个定值。

    然后就想到了二分。

    二分 (pre[n])的值,这样建边之后跑最长路或者最短路,进行差分约束,如果没有负环或者正环,当前(pre[n])的值就可以。

    1. 最长路建边

      对于 m1 个 1 类型的要求:(pre[r]-pre[l-1] geq k)

      (pre[r] geq pre[l-1] + k)

      建一条 (l-1) ——> (r) 权值为 (k) 的边

      m2 个 2 类型的要求:(pre[n]-(pre[r]-pre[l-1])geq k)

      (pre[l-1]>=pre[r] +k-pre[n])

      建一条 (r) ——> (l-1) 的权值为 (k-pre[n])的边

      注意隐含条件 (pre[i]-pre[i-1]geq 0)(pre[i]-pre[i-1]leq 1)(pre[i-1]-pre[i]geq-1)

      分别为这两个不等式建边:

      (i-1)(i) 建立一条权值为 (0) 的边

      (i)(i-1) 建立一条权值为 (-1) 的边

      因为我们二分 (pre[n])的值,所以还要加上一个条件:
      (pre[n]-pre[0]==k),我们把这个条件变成两个不等式:

      (pre[n]-pre[0]leq k)(pre[0]-pre[n]geq -k)

      (pre[n]-pre[0]geq k)

      再分别建边

    2. 最短路建边

      不等式其实和最长路列出来的一样,只是把(geq) 变成(leq)

    比赛的时候写的是最长路疯狂T。

    看题解发现,用最长路可以用SLF优化一下SPFA。

    如果跑最短路如果遇到 (dis) 值小于 0就可以判断出负环,因为 (i)(i-1) 有一条边权为 0 的边,这样就构成了负环。

    首先写了一个最短路的代码,187ms 过了

    最短路+提前判环 187ms

    /*
     * @Autor: valk
     * @Date: 2020-08-11 12:38:37
     * @LastEditTime: 2020-10-02 20:37:12
     * @Description: 如果邪恶  是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
     */
    #include <bits/stdc++.h>
    #define fuck system("pause")
    #define pb emplace_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int mod = 1e9 + 7;
    const int seed = 12289;
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int N = 3e3 + 10;
    
    struct Edge {
        int to, next, val;
    } edge[N * 100];
    int tot, head[N];
    void add(int u, int v, int val)
    {
        edge[tot] = { v, head[u], val };
        head[u] = tot++;
    }
    struct note {
        int l, r, k;
    } a[N], b[N];
    int dis[N], vis[N], cnt[N];
    int n, m1, m2;
    int judge(int x)
    {
        memset(head,-1,sizeof(head));
        tot=0;
        for(int i=1;i<=n;i++){
            add(i,i-1,0);
            add(i-1,i,1);
        }
        for(int i=1;i<=m1;i++){
            add(a[i].r,a[i].l-1,-a[i].k);
        }
        for(int i=1;i<=m2;i++){
            add(b[i].l-1,b[i].r,x-b[i].k);
        }
        add(0,n,x);
        add(n,0,-x);
        for(int i=0;i<=n;i++){
            dis[i]=inf;
            vis[i]=cnt[i]=0;
        }
        dis[0]=0;
        queue<int>q;
        q.push(0);
        vis[0]=cnt[0]=1;
        while(!q.empty()){
            int u =q.front();
            q.pop();
            vis[u]=0;
            for(int i=head[u];i!=-1;i=edge[i].next){
                int v =edge[i].to,val=edge[i].val;
                if(dis[v]>dis[u]+val){
                    dis[v]=dis[u]+val;
                    if(dis[v]<0) return 0;
                    if(!vis[v]){
                        if(++cnt[v]>n+1){
                            return 0;
                        }
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        return 1;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%d%d%d", &n, &m1, &m2);
            for (int i = 1; i <= m1; i++) {
                scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].k);
            }
            for (int i = 1; i <= m2; i++) {
                scanf("%d%d%d", &b[i].l, &b[i].r, &b[i].k);
            }
            int l=0,r=n,ans;
            while(l<=r){
                int mid=(l+r)/2;
                if(judge(mid)){
                    ans=mid;
                    r=mid-1;
                }else{
                    l=mid+1;
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    然后写了一个最长路的SLF优化:

    这随缘过题。。。

    跟假的一样。

    最长路+SLF优化 998ms

    /*
     * @Autor: valk
     * @Date: 2020-08-11 12:38:37
     * @LastEditTime: 2020-10-03 17:25:30
     * @Description: 如果邪恶  是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
     */
    #include <bits/stdc++.h>
    #define fuck system("pause")
    #define pb emplace_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int mod = 1e9 + 7;
    const int seed = 12289;
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int N = 3e3 + 10;
    
    struct Edge {
        int to, next, val;
    } edge[N * N];
    int tot, head[N];
    void add(int u, int v, int val)
    {
        edge[tot] = { v, head[u], val };
        head[u] = tot++;
    }
    struct note {
        int l, r, k;
    } a[N], b[N];
    int dis[N], vis[N], cnt[N];
    int n, m1, m2;
    int judge(int x)
    {
        for (int i = 0; i <= n; i++) {
            head[i] = -1;
        }
        tot = 0;
        for (int i = 1; i <= m1; i++) {
            add(a[i].l - 1, a[i].r, a[i].k);
        }
        for (int i = 1; i <= m2; i++) {
            add(b[i].r, b[i].l - 1, b[i].k - x);
        }
        for (int i = 1; i <= n; i++) {
            add(i - 1, i, 0);
            add(i, i - 1, -1);
        }
        add(0, n, x);
        add(n, 0, -x);
        for (int i = 0; i <= n; i++) {
            dis[i] = -inf;
            vis[i] = cnt[i] = 0;
        }
        vis[0] = cnt[0] = 1;
        dis[0] = 0;
        deque<int> q;
        q.push_back(0);
        while (!q.empty()) {
            int u = q.front();
            q.pop_front();
            vis[u] = 0;
            for (int i = head[u]; i != -1; i = edge[i].next) {
                int v = edge[i].to, val = edge[i].val;
                if (dis[v] < dis[u] + edge[i].val) {
                    dis[v] = dis[u] + edge[i].val;
                    if (!vis[v]) {
                        if (++cnt[v] > n + 1)
                            return 0;
                        vis[v] = 1;
                        if (!q.empty() && dis[v] > dis[q.front()]) {
                            q.push_front(v);
                        } else {
                            q.push_back(v);
                        }
                    }
                }
            }
        }
        return 1;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%d%d%d", &n, &m1, &m2);
            for (int i = 1; i <= m1; i++) {
                scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].k);
            }
            for (int i = 1; i <= m2; i++) {
                scanf("%d%d%d", &b[i].l, &b[i].r, &b[i].k);
            }
            int l = 0, r = n, ans;
            while (l <= r) {
                int mid = (l + r) / 2;
                if (judge(mid)) {
                    ans = mid;
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    今天发现了一个博客也写了SLF优化的最长路,然后用他的代码跑了跑,
    只用了93ms

    然后我开始了照着代码改代码的提交之路。

    各种魔改最后发现竟然是判断负环的地方:

    这是我写的判断负环的地方

    这是博主写的:

    然后我把我写的最长路也改成了这个样子

    我的天哪,从998ms 变成了61ms

    代码

    最长路+SLF优化 +更改判环位置 61ms

    /*
     * @Autor: valk
     * @Date: 2020-08-11 12:38:37
     * @LastEditTime: 2020-10-03 17:49:22
     * @Description: 如果邪恶  是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
     */
    #include <bits/stdc++.h>
    #define fuck system("pause")
    #define pb emplace_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int mod = 1e9 + 7;
    const int seed = 12289;
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int N = 3e3 + 10;
    
    struct Edge {
        int to, next, val;
    } edge[N * N];
    int tot, head[N];
    void add(int u, int v, int val)
    {
        edge[tot] = { v, head[u], val };
        head[u] = tot++;
    }
    struct note {
        int l, r, k;
    } a[N], b[N];
    int dis[N], vis[N], cnt[N];
    int n, m1, m2;
    int judge(int x)
    {
        for (int i = 0; i <= n; i++) {
            head[i] = -1;
        }
        tot = 0;
        for (int i = 1; i <= m1; i++) {
            add(a[i].l - 1, a[i].r, a[i].k);
        }
        for (int i = 1; i <= m2; i++) {
            add(b[i].r, b[i].l - 1, b[i].k - x);
        }
        for (int i = 1; i <= n; i++) {
            add(i - 1, i, 0);
            add(i, i - 1, -1);
        }
        add(0, n, x);
        add(n, 0, -x);
        for (int i = 0; i <= n; i++) {
            dis[i] = -inf;
            vis[i] = cnt[i] = 0;
        }
        vis[0] = cnt[0] = 1;
        dis[0] = 0;
        deque<int> q;
        q.push_back(0);
        while (!q.empty()) {
            int u = q.front();
            q.pop_front();
            vis[u] = 0;
            for (int i = head[u]; i != -1; i = edge[i].next) {
                int v = edge[i].to, val = edge[i].val;
                if (dis[v] < dis[u] + edge[i].val) {
                    dis[v] = dis[u] + edge[i].val;
                    cnt[v] = cnt[u] + 1;
                    if (cnt[v] > n + 1)
                        return 0;
                    if (!vis[v]) {
                        vis[v] = 1;
                        if (!q.empty() && dis[v] > dis[q.front()]) {
                            q.push_front(v);
                        } else {
                            q.push_back(v);
                        }
                    }
                }
            }
        }
        return 1;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%d%d%d", &n, &m1, &m2);
            for (int i = 1; i <= m1; i++) {
                scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].k);
            }
            for (int i = 1; i <= m2; i++) {
                scanf("%d%d%d", &b[i].l, &b[i].r, &b[i].k);
            }
            int l = 0, r = n, ans;
            while (l <= r) {
                int mid = (l + r) / 2;
                if (judge(mid)) {
                    ans = mid;
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    /*
    10
    4 1 1
    2 3 1
    2 3 2
    
    4 4 0
    1 1 1
    2 2 1
    3 3 1
    4 4 1
    
    5 3 0
    1 3 2
    2 4 1
    3 5 2
    */
    
  • 相关阅读:
    vs2010 怎样设置文本编辑窗口和解决方案资源管理器同步?
    google浏览器如何导出书签
    C#面试题
    Inkscape tricks
    jupyter-notebook kernel died
    matplotlib 显示中文
    Ubuntu+apt-get update时的源
    vscode设置出错, 无法自动补全
    Ubuntu+vscode打不开
    Ubuntu+Firefox总是打不开网页
  • 原文地址:https://www.cnblogs.com/valk3/p/13765218.html
Copyright © 2011-2022 走看看