[USACO13OPEN] 照片Photo
果然是秒想差分约束跑spfa啊qwq。。。。。
但是万恶的出题人他卡spfa啊(关于spfa,它死了),于是我们的差分约束只能跑60分(开O2跑70)
这是我的60分程序。。。(感觉不需要解释什么了吧,就是差分约束的连边)
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#define MAXN 300010
using namespace std;
inline int read()
{
int f=1,x=0; char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,edge_number;
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
int dis[MAXN],done[MAXN],len[MAXN],head[MAXN];
inline void add(int from,int to,int dis)
{
edge[++edge_number].dis=dis;
edge[edge_number].to=to;
edge[edge_number].nxt=head[from];
head[from]=edge_number;
}
inline void spfa(int x)
{
queue<int>q;
for(int i=1;i<=n;i++) dis[i]=2147483647,done[i]=0;
q.push(x),done[x]=1,dis[x]=0,len[x]=1;
while(!q.empty())
{
int u=q.front();
q.pop(),done[u]=0;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].dis)
{
dis[v]=dis[u]+edge[i].dis;
if(done[v]==0)
{
len[v]=len[u]+1;
if(len[v]>n)
{
cout<<"-1"<<endl;
exit(0);
}
q.push(v);
done[v]=1;
}
}
}
}
}
int main()
{
n=read(),m=read();
for(register int i=1;i<=m;i++)
{
int u,v;
u=read(),v=read();
add(v,u-1,-1);
add(u-1,v,1);
}
for(int i=1;i<=n;i++)
{
add(i,i-1,0);
add(i-1,i,1);
}
spfa(0);
printf("%d\n",dis[n]);
return 0;
}
之后看了神仙题解发现原来还可以用DP搞。。。。
但是蒟蒻不会呀qwq 所以我只能抄借鉴dalao的题解了。。。。。
但是有些大佬他们呢太巨了,写的解释不是很详细。。所以蒟蒻来补一个自认为比较详细的题解吧qwq
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 500010
using namespace std;
struct Messi{int l,r;}a[MAXN];
int i,j,head,tail;
int n,m,rr[MAXN],ll[MAXN],q[MAXN],f[MXAN];
int main()
{
cin>>n>>m;
for (i=1;i<=n+1;i++) rr[i]=i-1;
//预处理,每个点对应的j区间的右端点初始化为它左边一位的位置
for (i=1;i<=m;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
//输入区间范围
ll[a[i].r+1]=max(ll[a[i].r+1],a[i].l);
//显然它右端点向右距离一个单位长度的点不包含在它这个区间内
//所以要判断当前区间的左端点是否比那一个点先前记录区间的左端点大,来确定是否更新答案
rr[a[i].r]=min(rr[a[i].r],a[i].l-1);
//同理,见上面blog解释,更新答案
}
//因为上面是根据输入来更新左右端点的,所以可能更新不完
for (i=n;i>=1;i--) rr[i]=min(rr[i],rr[i+1]);
//因为右端点是根据每个区间最多只能放一个的限定来摆放的
//所以我们要从后往前递推更新
for (i=2;i<=n+1;i++) ll[i]=max(ll[i],ll[i-1]);
//左端点是根据每个区间至少要放一个的限定来摆放的
//所以从前往后递推更新
j=1;
head=tail=1;
//单调队列
for (i=1;i<=n+1;i++)
{
while (j<=rr[i]&&j<=n)
{
if (f[j]==-1)
{j++;continue;}//如果该状态不合法就不能放在队列里面
while (f[j]>f[q[tail]]&&tail>=head) tail--;
//维护单调递增
++tail;
q[tail]=j;
j++;
}
//在队列中加入所有合法解
while (q[head]<ll[i]&&head<=tail) head++;
//去除不在区间的解
if (head<=tail) f[i]=f[q[head]]+(i!=n+1?1:0);
else f[i]=-1;
//如果队列中仍然有数,那么更新答案
//最后一个是来判断答案的,所以只能加0
//否则此状态无解(也就是这个地方不能摆放)
}
cout<<f[n+1];
}