首页 > 其他分享 >[USACO13OPEN] Photo G 题解

[USACO13OPEN] Photo G 题解

时间:2024-09-05 10:50:07浏览次数:6  
标签:now int 题解 染色 USACO13OPEN Photo include mx dis

前言

题目链接:洛谷

题意简述

一个长度为 \(n\) 的序列,有一些位置染了色。现给出 \(m\) 条限制,第 \(i\) 条限制为 \(l_i \sim r_i\) 中有且仅有一个位置染色。求出满足这 \(m\) 中条件,染色位置个数最多为多少。

\(n \leq 2 \times 10^5\),\(m \leq 10^5\)。

题目分析

方法 \(1\):差分约束

区间问题使用前缀和,退化成关于两端点的限制:\(v_{r_i} - v_{l_i - 1} = 1\),其中 \(v_i\) 表示 \(1 \sim i\) 中有多少染色的位置。以及题目本身的限制:\(v_{i} - v_{i - 1} \in [0, 1]\),建图后差分约束跑一跑即可。

我们求 \(\max v_n\),使用最短路算法。

无解情况等价于存在负环,最短路不存在。

但是显然会超时,如何优化?SPFA 经典优化:SLF 优化。嗯,还差一个点,循环次数超过魔法数字 \(1736520\) 就无解。就水过去了。

方法 \(2\):动态规划

差分约束显然不是正解。序列问题,决策是当前点是否染色,可以使用 DP 解决。

我们设 \(f_i\) 表示 \(i\) 染色时 \(1 \sim i\) 染色的位置的个数的最大值。转移的时候枚举上一次染色的位置 \(j\),有转移方程:

\[f_i = \max _ {\text{meet given conditions}} \{ f_j \} + 1 \]

时间复杂度什么的先不谈,考虑怎么判断一个 \(j\) 是否合法。

有一个 trick:“恰好”等价于“不少于并且不大于”

对于本题,恰好区间染色了一个位置,拆成两个限制,即至少染色一个位置,至多染色一个位置。

先考虑前者。由于 \((i, j)\) 中没有染色,如果其中存在一个限制区间就会不合法。所以我们求得 \(mi_x\) 表示右端点在 \(x\) 左边,左端点的最大值。\(j\) 需要满足 \(j \geq mi_i\)。

再考虑后者。如果一个区间同时包括了 \(i\) 和 \(j\),那么也是不合法的。我们求得 \(mx_x\) 表示右端点在 \(x\) 及右边,左端点的最小值。\(j\) 需要满足 \(j \lt mx_i\)。

预处理扫一扫是简单的。

转移方程变成:

\[\large f_i = \max _ {j = mi_i} ^ {mx_i - 1} \{ f_j \} + 1 \]

由于 \(mx\) 和 \(mi\) 单调不降,直接上单调队列就行了。

注意到我们不能直接取 \(f\) 的最大值作为答案,因为我们需要满足所有 \(m\) 条限制。不妨用 \(f_{n + 1}\) 统计答案,这样就能完整考虑到所有限制,并求出最大值了。

时间复杂度:\(\Theta(n + m)\)。

代码

方法 \(1\):差分约束

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

const int N = 200010, M = 100010;

struct Graph {
    struct node {
        int to, len, nxt;
    } edge[N * 2 + M * 2];
    int tot = 1, head[N];
    void add(int u, int v, int w) {
        edge[++tot] = {v, w, head[u]};
        head[u] = tot;
    }
    inline node & operator [] (const int x) {
        return edge[x];
    }
} xym;

void smller(int u, int v, int w) {
    xym.add(v, u, w);
}

void bigger(int u, int v, int w) {
    smller(v, u, -w);
}

void equals(int u, int v, int w) {
    bigger(u, v, w);
    smller(u, v, w);
}

int n, m;

int dis[N], cnt[N];
bool inq[N];

bool SPFA() {
    int yzh_i_love_you = 0;
    memset(dis, 0x3f, sizeof dis);
    deque<int> Q; Q.push_front(0), dis[0] = 0, cnt[0] = 1;
    inq[0] = true;
    while (!Q.empty()) {
        int now = Q.front(); Q.pop_front();
        inq[now] = false;
        for (int i = xym.head[now]; i; i = xym[i].nxt) {
            int to = xym[i].to, w = xym[i].len;
            if (dis[to] > dis[now] + w) {
                dis[to] = dis[now] + w;
                cnt[to] = cnt[now] + 1;
                if (cnt[to] > n + 1 || ++yzh_i_love_you > 1736520) return false;
                if (!inq[to]) {
                    inq[to] = true;
                    if (!Q.empty() && dis[to] < dis[Q.front()]) Q.push_front(to);
                    else Q.push_back(to);
                }
            }
        }
    }
    return true;
}

signed main() {
    #ifndef XuYueming
    freopen("photo.in", "r", stdin);
    freopen("photo.out", "w", stdout);
    #endif
    scanf("%d%d", &n, &m);
    for (int i = 1, l, r; i <= m; ++i) {
        scanf("%d%d", &l, &r);
        equals(r, l - 1, 1);
    }
    for (int i = 1; i <= n; ++i) {
        bigger(i, i - 1, 0);
        smller(i, i - 1, 1);
    }
    if (!SPFA()) return puts("-1"), 0;
    printf("%d", dis[n]);
    return 0;
}

方法 \(2\):动态规划

#include <cstdio>
#include <iostream>
using namespace std;

const int N = 200010;

int n, m, mx[N], mi[N];
int Q[N], head, tail = -1;
int f[N];

signed main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n + 1; ++i) mx[i] = i;
    for (int i = 1, l, r; i <= m; i++) {
        scanf("%d%d", &l, &r);
        mx[r] = min(mx[r], l);
        mi[r + 1] = max(mi[r + 1], l);
    }
    for (int i = n; i >= 1; --i) mx[i] = min(mx[i], mx[i + 1]);
    for (int i = 2; i <= n + 1; ++i) mi[i] = max(mi[i], mi[i - 1]);
    for (int i = 1, j = 0; i <= n + 1; ++i) {
        while (j < mx[i]) {
            if (f[j] != -1) {
                while (head <= tail && f[Q[tail]] <= f[j]) --tail;
                Q[++tail] = j;
            }
            ++j;
        }
        while (head <= tail && Q[head] < mi[i]) ++head;
        if (head <= tail) f[i] = f[Q[head]] + 1;
        else f[i] = -1;
    }
    if (f[n + 1] == -1) puts("-1");
    else printf("%d\n", f[n + 1] - 1);
    return 0;
}

标签:now,int,题解,染色,USACO13OPEN,Photo,include,mx,dis
From: https://www.cnblogs.com/XuYueming/p/18397834

相关文章

  • Baby Ehab Plays with Permutations 题解
    前言题目链接:Codeforces;洛谷。题意简述你有一个长度为\(n\)的序列\(p\)满足\(p_i=i\),你可以进行\(x\)次操作,每次操作找到两个不同的\(i,j\)并且交换\(p_i,p_j\),问最终有几个可能的序列。分别求出\(x=1,\ldots,k\)时的答案。\(1\len\le10^9\),\(1\lek\le......
  • Marbles 题解
    前言题目链接:Codeforces;洛谷。题意简述给定长度为\(n\)的序列\(a\),你可以交换相邻元素,请问最少交换多少次使得序列连续,即对于每种颜色,其在序列中出现的位置都是连续一段。\(m=\max\{a_i\}\leq20\),\(n\leq4\times10^5\)。题目分析对于“连续的序列”,不放看做......
  • [POI2014] RAJ-Rally 题解
    前言题目链接:Hydro&bzoj;黑暗爆炸;洛谷。题意简述DAG求删点后最长路的最小值。\(n\leq5\times10^5\),\(m\leq10^6\)。题目分析其实对于删点/边加查询最长/短路的套路是有的。比如:故乡的梦、桥。本题也类似。我们考虑,如果删除的边不在原来最长路上,那么删之后的......
  • CF1941场题解
    RudolfandtheTicket算法:枚举。题意简述:从\(a\)数组中和\(b\)数组中各选出一个数,使得它们的和不超过\(k\),求选法数量。考虑直接枚举每一种可能的搭配即可。Rudolfand121算法:贪心。题意简述:定义一次操作为,该位置上的数减去\(2\),其前一个和后一个位置(必须存在)上的数......
  • 2023 ICPC 合肥题解
    gymD.BalancedArray\(\star\)赛时做法枚举前缀维护合法的\(k\)感性上\(k\)越大需要满足的式子越少,只保留最大的\(\log\)个\(k\),可以通过std枚举\(k\),合法的\(l\)一定是一个左端点为\(2k+1\)的区间,二分右端点等式\(\forall1\lei\lel-2k,a_{i}+a_{i+2k}=2a......
  • Codeforces Round 971 (Div. 4) ABCD题详细题解(C++,Python)
    前言:    本文为CodeforcesRound971(Div.4)ABCD题的题解,包含C++,Python语言描述,觉得有帮助或者写的不错可以点个赞    比赛打了没一半突然unrated了就不是很想继续写了,早起写个题解    (之前的div3也没复盘,哎真菜)目录题A:题目大意和解题......
  • AtCoder Beginner Contest 369 题ABCD详细题解--包含解题思路和两种语言(C++,Python)
    前言:    本文为AtCoderBeginnerContest369题ABCD详细题解,包括题目大意,详细的解题思路和两种语言描述,觉的有帮助或者写的不错可以点个赞几天前的比赛拖的有点久了比赛题目连接:Tasks-AtCoderBeginnerContest369目录题A:题目大意和解题思路:代码(C++):......
  • 洛谷 B3645 数列前缀和 2 题解
    前缀知识:枚举,费马小定理,逆元,线性乘法逆元,线段树(?)。解法1:暴力如题。暴力枚举即可,30分。由于太简单,不给代码。解法2:前缀积+费马小定理+逆元由于涉及静态区间,可以想到前缀积。前缀积公式为\(q_r/q_{l-1}\),除法恰好可以用逆元来算。直接写即可。不会超时,因为时间为\(O(n\logp)\)......
  • Codeforces Round 971 (Div. 4) E 题解析
    #E题Klee'sSUPERDUPERLARGEArray!!!题目描述思路:对于这道题,首先观察到题目求的是最小可能值,而且数据的范围是1e9范围,所以首先可以考虑的方法就是O(logn)的方法,而适合求最值的方法无疑就是二分搜索,可以通过二分来不断缩小答案的区间......
  • 常见问题解决 --- 如何给一个不支持配置代理的程序抓取https流量数据
    比如我有一个C#编写票务系统,它内嵌浏览器功能,我想抓取它的流量,但是这个客户端不支持配置代理设置解决办法:1.安装配置proxifier开启全局代理服务。安装好后网上有激活码激活一下,点击profile-proxyserver,添加一个代理服务器127.0.0.1,端口8080,协议https。点击profile-proxifi......