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;
}
這題還可以上線段樹貪心,也是比較容易的做法。