【JZOJ4302】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
分析一下输入,它是由A个I,B个O,C个I,D个O,E个I。
我们把I看做1,O看做0,把原序列转换成一个01串,把每一项变成前后异或后的值,组成一个新的01串,然后我们观察这个串。
它是由许多0和4个1组成的,而1的位置就是原来01的交界处。
而题目就转化为将4个1变为0的最少代价。
然后我们尝试一个操作(
l
,
于是我们对于每个操作,给
为什么呢?
我们假设有一条道路
然而这题有4个1,我们就两两做匹配更新答案就好了。
由于这题边数较多,注意最短路的时间复杂度。
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 500020
#define inf 214748364734454ll
#define ll long long
using namespace std;
int a[6];
int cz[N][2];
bool b[N];
bool nw[N];
int to[N],next[N],last[N],val[N],num=0;
ll dis[N];
bool vis[N];
int p[3][4]=
{
{1,2,3,4},
{1,3,2,4},
{1,4,2,3},
};
void link(int x,int y,int c)
{
num++;
to[num]=y;
next[num]=last[x];
last[x]=num;
val[num]=c;
}
int d[N*2];
void spfa(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,60,sizeof(dis));
dis[s]=0;
vis[s]=true;
d[1]=s;
int l=0,r=1;
while(l<r)
{
l++;
int x=d[l];
for(int i=last[x];i;i=next[i])
{
int v=to[i];
if(dis[x]+val[i]<dis[v])
{
dis[v]=dis[x]+val[i];
if(!vis[v])
{
d[++r]=v;
vis[v]=true;
}
}
}
vis[x]=false;
}
}
int main()
{
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
bool q=1;
fo(i,1,5) scanf("%d",&a[i]),a[i]+=a[i-1];
int n;
cin>>n;
fo(i,1,n)
{
int x,y;
scanf("%d %d",&x,&y);
link(x-1,y,y-x+1);
link(y,x-1,y-x+1);
}
ll ans=inf;
fo(i,0,2)
{
spfa(a[p[i][0]]);
ll t1=dis[a[p[i][1]]];
spfa(a[p[i][2]]);
ll t2=dis[a[p[i][3]]];
ans=min(ans,t1+t2);
}
if(ans>=inf) cout<<-1;
else cout<<ans;
}