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;
}
这题还可以上线段树贪心,也是比较容易的做法。