题意
给五个正整数 A B C D E,表示现在有一个01序列,由A个1,B个0,C个1,D个0,E个1拼接而成。现在有m种操作方式,每个操作给定Li,Ri,表示把Li到Ri这一段取反,它的代价是Ri-Li+1,现在求把整个序列全部变成1的最小代价。
数据范围
A,B,C,D,E,m≤105 Li,Ri≤109
解法
这个题我们首先可以想到异或差分,第i个位置相当于序列的第i个位置和i-1个位置异或,那么这个序列最初态就变成了包含4个1的序列(分别在A+1,B+1,C+1,D+1),然后对于每个操作就是把序列里的Li+1,Ri+1两个位置取反。
那么最终目标就是把整个序列变成0,我们考虑用最短路来解决。
对于每个操作,我们从Li+1向Ri+1连无向边,边权就是这个操作的代价。那么要解决这个问题我们可以把第一个位置的1(也就是A+1这个位置)依次与另外三个位置匹配,然后用最短路跑出它匹配的位置的答案,再计算另外两个匹配的答案,最后看是否存在路径(也就是最短路小于inf ),如果存在就更新答案。
这个题还有一个变种,就是01段不止5段,
n≤400 m≤5000
这个题我们还是异或差分建图,然后用floyd跑最短路,那么可以得到每个1与其他1匹配的权值,所以这个题就变成了一般图最小权匹配,用带权带花树就可以解决了(虽然我不会写)
收获
首先用异或差分把修改从区间修改变成单点修改,然后通过观察可以发现这是一个匹配问题,用最短路求两点匹配最小代价。
考点:
差分思想 模型转化 最短路
易错点:
爆int inf不够大 空间应该是A+B+C+D+E= 5×105
#include <bits/stdc++.h>
#define ll long long
#define lf double
#define E complex<lf>
#define inf 0x7fffffff
#define eps 1e-8
#define pa pair<int,int>
#define pb push_back
#define ms(x,y) memset(x,y,sizeof(x))
#define l(x) (x<<1)
#define r(x) (x<<1|1)
#define mod 1000000007
#define N 1000010
using namespace std;
inline int read() {
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
int a,b,c,d,e,n,m;
int nxt[N<<1],head[N],to[N<<1],len[N<<1],tot=0;
int s1,s2,s3,s4;
inline void link(int x,int y,int z) {
nxt[++tot]=head[x],head[x]=tot,to[tot]=y,len[tot]=z;
}
int que[N];
ll dis[N];
bool vis[N];
inline void spfa(int s) {
ms(dis,0x3f);
int l,r;
l=r=0;
dis[s]=0,que[++r]=s,vis[s]=1;
while (l<r) {
int x=que[(++l)%n];vis[x]=0;
for (int i=head[x]; i; i=nxt[i]) {
int j=to[i];
if (dis[j]>dis[x]+len[i]) {
dis[j]=dis[x]+len[i];
if (!vis[j]) vis[j]=1,que[(++r)%n]=j;
}
}
}
}
int main() {
freopen("earthworm.in","r",stdin);
freopen("earthworm.out","w",stdout);
a=read(),b=a+read(),c=b+read(),d=c+read(),e=d+read();
s1=a+1,s2=b+1,s3=c+1,s4=d+1,n=a+b+c+d+e;
m=read();
for (int i=1; i<=m; i++) {
int l=read(),r=read();
link(l,r+1,r-l+1),link(r+1,l,r-l+1);
}
ll ans=1ll<<60,tmp=0;
spfa(s1);tmp+=dis[s2];
spfa(s3);tmp+=dis[s4];
ans=min(ans,tmp),tmp=0;
spfa(s1);tmp+=dis[s3];
spfa(s2);tmp+=dis[s4];
ans=min(ans,tmp),tmp=0;
spfa(s1);tmp+=dis[s4];
spfa(s2);tmp+=dis[s3];
ans=min(ans,tmp);
if (ans==1ll<<60) return puts("-1"),0;
cout << ans << endl;
}
/*
1 2 3 4 5
3
2 3
2 6
4 10
*/