Atcoder ABC216G 01Sequence 题解 [ 蓝 ] [ 差分约束 ]

01Sequence:比较板的差分约束,但有一个很妙的转化。

朴素差分约束

\(x_i\) 表示第 \(i\) 位的前缀和。

我们要最小化 \(1\) 的个数,就要求最小解,就要求最长路。因为约束条件都是大于等于号,所以求最长路才能满足所有条件。求最大解也是同理。

我们可以对于每一个条件,列出如下不等式:

\[x_b \ge x_{a-1}+c \]

\[x_{i} \ge x_{i+1}-1 \]

\[x_{i+1} \ge x_i+0 \]

显然我们跑一遍 spfa 最长路即可求解。

时间复杂度最劣 \(O(n^2)\),容易被卡。

优化差分约束

我们考虑正难则反,把制约必须使用 spfa 的负权边化为正权边,就能跑 dijkstra 了。

\(x_i\) 表示第 \(i\) 位的 \(0\) 的个数的前缀和。

于是我们要求最大解,也就是最短路。

不等式如下:

\[x_b \le x_{a-1}+(b-a+1)-c \]

\[x_{i+1} \le x_{i}+1 \]

\[x_i \le x_{i+1}+0 \]

这样就可以跑 dijkstra 了。

时间复杂度 \(O(n \log n)\)

用 dijkstra 或者缩点拓扑来优化差分约束的 spfa 是很常用的优化,一定要掌握。因为他们的本质都是求最短路或最长路,差分约束只是一种思想而已。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
int n,m,d[200005];
bitset<200005>vis;
vector<pi>g[200005];
priority_queue<pi,vector<pi>,greater<pi> >q;
void dijkstra(int s)
{
    memset(d,0x3f,sizeof(d));
    vis.reset();
    q.push({0,s});
    d[s]=0;
    while(!q.empty())
    {
        auto y=q.top();
        q.pop();
        int u=y.se;
        if(vis[u])continue;
        vis[u]=1;
        for(auto ed:g[u])
        {
            int v=ed.fi,w=ed.se;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                q.push({d[v],v});
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a-1].push_back({b,b-a+1-c});
    }
    for(int i=0;i<n;i++)
    {
        g[i+1].push_back({i,0});
        g[i].push_back({i+1,1});
    }
    dijkstra(0);
    for(int i=1;i<=n;i++)cout<<(1-(d[i]-d[i-1]))<<" ";
    return 0;
}

这题还可以上线段树贪心,也是比较容易的做法。

posted @ 2024-11-14 23:38  KS_Fszha  阅读(0)  评论(0编辑  收藏  举报