JOI 2014-2015 D1T4 异或差分+最短路

题意

给五个正整数 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,m105   Li,Ri109

解法

这个题我们首先可以想到异或差分,第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段, n400  m5000
这个题我们还是异或差分建图,然后用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
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值