(差分约束+优化)[abc216_g]01Sequence

HZOJ
洛谷

写在前面

位是接德梅号衣粘。 ————萝怊蔷

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

附:差分约束实际上是通过将所有限制差分解出一组特解,然后由于差分的性质,所有项数加上或减去同一个数得到的解依然满足条件。大概就类似于贝祖定理?

posted @ 2025-08-25 21:48  _dlwlrma  阅读(17)  评论(0)    收藏  举报