Atcoder ABC 216 G 01Sequence 題解 [ 藍 ] [ 差分約束 ]

KS_Fszha發表於2024-11-14

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;
}

這題還可以上線段樹貪心,也是比較容易的做法。

相關文章