ZOJ-4028 LIS (差分约束)

题目链接:ZOJ-4028 LIS

题意

有一个长度为 \(n\ (1\le n \le 10^5)\) 的序列 \(a\),对于所有的 \(i\in [1,n]\) ,已知 \(f_i,\ l_i,\ r_i\) ,代表序列 \(a\) 中末尾元素为 \(a_i\) 的最长上升子序列长度为 \(f_i\) ,且 \(l_i \le a_i \le r_i\) ,求序列 \(a\)


思路

找出 \(a_i\) 的所有约束条件:

  1. \(i'\)\(i\) 前面满足 \(f_{i'}=f_i\) 的离 \(i\) 最近的位置,\(a_i\) 的值需要满足 \(a_i \le a_{i'}\) ;
  2. \(i''\)\(i\) 前面满足 \(f_{i''}+1=f_i\) 的离 \(i\) 最近的位置,\(a_i\) 的值需要满足 \(a_i>a_{i''}\) ;
  3. \(l_i\le a_i\le r_i\)

知道了每个 \(a_i\) 的约束条件,可以用差分约束求解(用 \(e(u,v,w)\) 代表从 \(u\)\(v\) 权值为 \(w\) 的有向边):

  1. \(a_i\le a_{i'}\) 可变形为 \(a_i\le a_{i'}+0\) ,连边 \(e(i',i,0)\) ;
  2. \(a_i>a_{i''}\) 可变形为 \(a_{i''}\le a_i-1\) ,连边 \(e(i,i'',-1)\) ;
  3. 新增一个源点 \(s\),令 \(a_s=0\)\(l_i\le a_i\le r_i\) 可变形为 \(a_s\le a_i-l_i,\ a_i\le a_s+r_i\) ,连边 \(e(i,s,-l_i),\ e(s,i,r_i)\)

建完图跑差分约束即可。


代码实现

#include <cstdio>
#include <cstring>
#include <queue>
using std::queue;
typedef long long LL;
const int maxn = 100010;
int head[maxn], tot;
LL dist[maxn];
int f[maxn], last[maxn];
bool inq[maxn];
struct Edge{
    int to, nex, val;
} edge[maxn<<2];
void add_edge(int u, int v, int w) {
    edge[++tot].nex = head[u];
    edge[tot].to = v;
    edge[tot].val = w;
    head[u] = tot;
}
void bfs_spfa(int s, int n) {
    memset(inq, 0, sizeof(bool) * (n+5));
    memset(dist, 0x3f, sizeof(LL) * (n+5));
    queue<int> que;
    que.push(s);
    inq[s] = true;
    dist[s] = 0;
    while (!que.empty()) {
        int u = que.front();
        que.pop();
        inq[u] = false;
        for (int i = head[u]; i; i = edge[i].nex) {
            int v = edge[i].to;
            if (dist[v] > dist[u] + edge[i].val) {
                dist[v] = dist[u] + edge[i].val;
                if (!inq[v]) {
                    inq[v] = true;
                    que.push(v);
                }
            }
        }
    }
    for (int i = 1; i <= n; i++) printf("%lld%c", dist[i], " \n"[i==n]);
}

int main() {
    int T, n;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        memset(last, 0, sizeof(int) * (n+5));
        memset(head, 0, sizeof(int) * (n+5));
        tot = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &f[i]);
            if (last[f[i]]) add_edge(last[f[i]], i, 0);
            if (f[i] > 1) add_edge(i, last[f[i]-1], -1);
            last[f[i]] = i;
        }
        for (int i = 1, l, r; i <= n; i++) {
            scanf("%d %d", &l, &r);
            add_edge(0, i, r);
            add_edge(i, 0, -l);
        }
        bfs_spfa(0, n);
    }
    return 0;
}
posted @ 2020-08-16 14:50  _kangkang  阅读(106)  评论(0编辑  收藏  举报