【NOIP2015模拟11.3】IOIOI卡片占卜

Description

K理事长很喜欢占卜,经常用各种各样的方式进行占卜。今天,他准备使用正面写着”I”,反面写着”O”的卡片为今年IOI的日本代表队占卜最终的成绩。
占卜的方法如下所示:
首先,选择5个正整数A,B,C,D,E。
将A+B+C+D+E张IOI卡片排成一行,最左侧的A张卡片正面朝上,接下来B张反面朝上,接下来C张卡片正面朝上,接下来D张反面朝上,最后E张正面朝上。如此排列的话,从左侧开始顺次为A张“I”,B张“O”,C张“I”,D张“O”,E张“I”。
在预先决定的N种操作中选出至少1种,然后按照任意顺序执行。(注:同种操作执行多次也是可以的。)这里,第i种操作(1<=i<=N)为【将从左数第Li张卡片到第Ri张卡片全部翻转】。翻转一张卡片需要1秒的时间,因此第i种操作耗时Ri-Li+1秒。
操作结束后,如果所有卡片都是正面朝上则占卜成功。K理事长不想翻多余的牌,因此在实际使用卡片占卜之前会先计算出是否存在占卜成功的可能性。进一步,如果占卜可能成功,他会计算出能使占卜成功所消耗的时间的最小值。
现在给出卡片的排列信息和预先决定的操作信息,请你写一个程序,计算出占卜能否成功,如果能成功,输出消耗时间的最小值。

Solution

把原序列变成相邻的两个的异或值,初始序列就只有4个1,那么每次操作只会更改l,r两个值,
问题就转换成了求这个序列全部为0时的最小代价(显然使原序列全为1的方案是没有的),
我们把每次的l,r连一条边,边权就是(r-l+1),一次合法的操作只能把两个点变成0,代价就是两个点的最短距离,
答案就是把4个1暴力分成两组的两两最短距离和,
重复的情况是有的,但答案一定不是最优的,

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define foi(i,a,b) for(i=a;i<=b;i++)
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fo1(i,a,b) for(int i=a;i>=b;i--)
//#define read(a) scanf("%d",&a)
using namespace std;
typedef long long LL;
const int N=500500;
const LL INF=9223372036854775800;
int read(int &n)
{
    char ch=getchar();
    while((ch!='-')&&((ch<'0')||(ch>'9')))ch=getchar();
    int q=0,w=1;if(ch=='-')w=-1,ch=getchar();
    while(ch>='0' && ch<='9')q=q*10+ch-48,ch=getchar();n=q*w;return n;
}
int m,n;
int a[5];
int A[N],B0,B[2*N][3];
int d[3*N];
LL d1[N],ans;
bool z[N];
void join(int q,int w,int e,int E)
{
    B[++B0][1]=w,B[B0][2]=e,B[B0][0]=A[q],A[q]=B0;
    if(E)join(w,q,e,0);
}
int spfa(int q)
{
    memset(d1,60,sizeof(d1));
    int i,j;
    z[q]=1;d1[q]=0;
    d[i=j=1]=q;
    while(i<=j)
    {
        q=d[i++];
        for(int k=A[q];k;k=B[k][0])
        {
            int w=B[k][1];
            if(d1[q]+B[k][2]<d1[w])
            {
                d1[w]=d1[q]+B[k][2];
                if(!z[w])z[w]=1,d[++j]=w;
            }
        }
        z[q]=0;
    }
}
int main()
{
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);
    int q;
    LL w;read(a[0]);
    fo(i,1,4) read(a[i]),a[i]+=a[i-1];
    read(n);
    fo(i,1,n)
    {
        w=read(q),read(q);
        join(w-1,q,q-w+1,1);
    }
    ans=INF; 
    spfa(a[0]),w=d1[a[1]],spfa(a[2]),w+=d1[a[3]];ans=min(ans,w);
    spfa(a[0]),w=d1[a[2]],spfa(a[1]),w+=d1[a[3]];ans=min(ans,w);
    spfa(a[0]),w=d1[a[3]],spfa(a[1]),w+=d1[a[2]];ans=min(ans,w);
    if(ans>=INF/3)printf("-1\n");
        else printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(最短路,SPFA)