(差分约束+优化)[abc216_g]01Sequence
写在前面
位是接德梅号衣粘。 ————萝怊蔷
[abc216_g]01Sequence 题解
思路
没有任何思路,看着是道绿题而自己完全没想法陷入深深地自我怀疑。然后点进题解区,发现是差分约束。何为差分约束?差分约束是一种特殊的\(n\) 元一次不等式,包含\(n\) 个变量及\(m\) 个约束式子,每个约束式子由两个变量作差构成,每个式子形如\(a_i−a_j≥k,k \text{是常数},1≤i,j≤n\)。由于转移式子满足三角不等式,所以一般将问题转化到图上在图上跑。
参考上面的定义,就可以在有限制的点间建边。本题中我们将序列转化到1的个数的前缀和序列。所以我们可以从\(l-1\) 向\(r\) 连一条权值为\(x\) 的边。同时我们不能忘记相邻两个点对答案的贡献要么是0要么是1,也要连边。然后连完边从0开始跑最长路(根据不等式的大小于关系)使得所有点间一定满足要求,如果到\(dis_n\) 存在合法答案则存在合法答案,当然本题一定存在合法答案。所以直接输出相邻两项的差即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10,maxm=2e6;
int n,m;
int tot,to[maxm],nxt[maxm],h[maxn],val[maxm];
inline void adde(int x,int y,int z){
to[++tot]=y;
nxt[tot]=h[x];
h[x]=tot;
val[tot]=z;
}
queue<int>q;
bool vis[maxn];
int dis[maxn];
void spfa(int x){
memset(dis,-0x3f,sizeof(dis));
dis[x]=0;
q.push(x);
while(q.size()){
x=q.front();
q.pop();
vis[x]=0;
for(int i=h[x],y=to[i];i;i=nxt[i],y=to[i])
if(dis[y]<dis[x]+val[i]){
dis[y]=dis[x]+val[i];
if(!vis[y]) q.push(y),vis[y]=1;
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,l,r,x;i<=m;i++) cin>>l>>r>>x,adde(l-1,r,x);
for(int i=0;i<=n;i++) adde(i,i+1,0),adde(i+1,i,-1);
spfa(0);
for(int i=1;i<=n;i++) cout<<dis[i]-dis[i-1]<<' ';
return 0;
}
由于存在负边权,所以我们用SPFA。但很可惜本题被卡了,无法通过。
考虑优化。由于本题中序列的每个位置只存在0/1两种状态,所以我们也可以将前缀和序列钦定为0的个数的前缀和。所以可以将边权全部限制在非负整数范围内。然后就是类似的思路跑一遍dijkstra。最后输出相邻两项前缀和的差异或1的值即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10,maxm=2e6;
int n,m;
int tot,to[maxm],nxt[maxm],h[maxn],val[maxm];
inline void adde(int x,int y,int z){
to[++tot]=y;
nxt[tot]=h[x];
h[x]=tot;
val[tot]=z;
}
priority_queue<pair<int,int>>q;
bool vis[maxn];
int dis[maxn];
void dij(int x){
memset(dis,0x3f,sizeof(dis));
dis[x]=0;
q.push({0,x});
while(q.size()){
x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=h[x];i;i=nxt[i]){
int y=to[i];
if(dis[y]>dis[x]+val[i]){
dis[y]=dis[x]+val[i];
q.push({-dis[y],y});
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,l,r,x;i<=m;i++) cin>>l>>r>>x,adde(l-1,r,r-l+1-x);
for(int i=0;i<=n;i++) adde(i+1,i,0),adde(i,i+1,1);
dij(0);
for(int i=1;i<=n;i++) cout<<((dis[i]-dis[i-1])^1)<<' ';
return 0;
}
附:差分约束实际上是通过将所有限制差分解出一组特解,然后由于差分的性质,所有项数加上或减去同一个数得到的解依然满足条件。大概就类似于贝祖定理?

浙公网安备 33010602011771号