2019 CCPC 哈尔滨站 A. Artful Paintings 差分约束+二分

Posted valk3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
*/










以上是关于2019 CCPC 哈尔滨站 A. Artful Paintings 差分约束+二分的主要内容,如果未能解决你的问题,请参考以下文章

2017 CCPC 哈尔滨站 HDU 6242

HDU - 6231 K-th Number (2017CCPC哈尔滨站 二分+尺取法)

2017CCPC-哈尔滨站 Hdu-6230 Palindrome Manacher 主席树

2017CCPC-哈尔滨站 Hdu-6242 Geometry Problem 计算几何 随机

2017CCPC Final (哈尔滨)

2017CCPC 哈尔滨 B