题解 P8098 [USACO22JAN] Tests for Haybales G
第一眼拿到这题,想到的应该直接就是差分约束!不妨令 ,对于每个条件(第 个点输入是 )是这样的约束关系:
再补充上:
按照以上三个条件连边即可。于是你发现样例就过了,提交发现居然 。然后发现 SPFA 死了。以下是 Luogu 分代码(通过 ):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+5;
const ll inf=100000000000000000;
ll dis[N],v[2*N];
int vis[N],n,m,k;
int cnt,nxt[2*N],t[2*N],h[2*N],num[2*N];
inline void add(int x,int y,int z) //x-y<=z
{
swap(x,y);
t[++cnt]=y;
v[cnt]=z;
nxt[cnt]=h[x];
h[x]=cnt;
}
queue <int> q;
void spfa(int s)
{
for (int i=1;i<=n;i++) dis[i]=inf,vis[i]=0;
dis[s]=0,vis[s]=1,num[s]=1;
q.push(s);
while (!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for (int i=h[u];i;i=nxt[i])
{
if (dis[t[i]]>dis[u]+v[i])
{
dis[t[i]]=dis[u]+v[i];
if (vis[t[i]]==0)
{
vis[t[i]]=1;
num[t[i]]++;
if (num[t[i]]>n)
{
printf("NO");
exit(0);
}
q.push(t[i]);
}
}
}
}
}
inline int read()
{
char C=getchar();
int F=1,ANS=0;
while (C<'0'||C>'9')
{
if (C=='-') F=-1;
C=getchar();
}
while (C>='0'&&C<='9')
{
ANS=ANS*10+C-'0';
C=getchar();
}
return F*ANS;
}
int main()
{
n=read(),k=n+1;
for (int i=1;i<=n;i++)
{
int x=read();
if (x!=i) add(x,i,k);
if (x!=n) add(i,x+1,-k-1);
}
for (int i=1;i<=n;i++) add(i,0,0);
for (int i=1;i<n;i++) add(i,i+1,0);
spfa(0);
printf("%d\n",k);
for (int i=1;i<=n;i++) printf("%lld\n",dis[i]+inf);
return 0;
}
因为存在负权边,所以不能使用 Dij 等其他最短路算法。那么怎么解决负权?
先把原序列切成几段,每碰到一个 的就切一下。显然段和段之间不影响。对于每一个段,我们再考虑分层。最后一个是第 层,其他的第 个就是 的层数再加一。
我们令第 层的初始权值为 。这样会发现,条件 中的 必然隔一层,那么 就消掉了。条件 中 割一层或两层,两层的话就不用考虑,否则 也消掉了。条件 对于同一层的照样做就行了。
于是原来的条件的权值只有 和 了,容易发现图变成了 DAG,因此可以线性求出最长路。
记得把每个数改为非负整数,还有段和段之间的间隔要注意。
// By: Little09
// Problem: P8098 [USACO22JAN] Tests for Haybales G
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8098
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+5;
const ll inf=100000000000000000;
ll dis[N],v[2*N],k;
int a[N];
bool used[N];
int n,m;
int cnt,nxt[2*N],t[2*N],h[2*N],d[N];
inline void add(int x,int y,int z) //x-y>=z
{
swap(x,y);
t[++cnt]=y;
v[cnt]=z;
nxt[cnt]=h[x];
h[x]=cnt;
d[y]++;
}
queue<int>q;
void topo(int s,int l,int r)
{
for (int i=l;i<=r;i++) dis[i]=0,used[i]=0;
q.push(s);
dis[s]=0;
for (int i=l;i<=r;i++) if (d[i]==0) q.push(i);
while (!q.empty())
{
int u=q.front();
q.pop();
for (int i=h[u];i;i=nxt[i])
{
d[t[i]]--;
dis[t[i]]=max(dis[t[i]],dis[u]+v[i]);
if (d[t[i]]==0) q.push(t[i]);
}
}
}
inline int read()
{
char C=getchar();
int F=1,ANS=0;
while (C<'0'||C>'9')
{
if (C=='-') F=-1;
C=getchar();
}
while (C>='0'&&C<='9')
{
ANS=ANS*10+C-'0';
C=getchar();
}
return F*ANS;
}
int deth[N];
void work(int l,int r,ll qwq)
{
deth[r]=0;
for (int i=0;i<=cnt;i++)
{
nxt[i]=0,v[i]=0,t[i]=0;
}
for (int i=l;i<=r;i++) h[i]=0,d[i]=0;
h[0]=0,d[0]=0;
cnt=0;
for (int i=r-1;i>=l;i--) deth[i]=deth[a[i]]+1;
for (int i=l;i<r;i++)
{
if (deth[i]==deth[i+1])
{
add(i+1,i,0);
}
}
for (int i=l;i<r;i++)
{
add(i,a[i],0);
if (a[i]+1<=r&&deth[a[i]+1]==deth[a[i]])
add(a[i]+1,i,1);
}
for (int i=l;i<=r;i++) add(i,0,0);
topo(0,l,r);
for (int i=l;i<=r;i++) dis[i]-=1ll*deth[i]*k;
for (int i=r;i>=l;i--) dis[i]-=dis[l];
for (int i=l;i<=r;i++) dis[i]+=qwq;
}
int main()
{
n=read(),k=n+1;
for (int i=1;i<=n;i++) a[i]=read();
int las=1;
ll tmp=0;
for (int i=1;i<=n;i++)
{
if (a[i]==i)
{
work(las,i,tmp+k+1);
las=i+1,tmp=dis[i];
}
}
printf("%lld\n",k);
for (int i=1;i<=n;i++) printf("%lld\n",dis[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构