1. 程式人生 > 遊戲資訊 >正式服4.2更新:瀾新面板上線,5英雄調整,兩野王變動,小喬加強

正式服4.2更新:瀾新面板上線,5英雄調整,兩野王變動,小喬加強

第一眼拿到這題,想到的應該直接就是差分約束!不妨令 \(K=n+1\),對於每個條件(第 \(i\) 個點輸入是 \(j\))是這樣的約束關係:

\[a_j-a_i\le K(1) \] \[a_{j+1}-a_i\ge K+1(2) \]

再補充上:

\[a_{i+1}-a_i \ge 0(3) \]

按照以上三個條件連邊即可。於是你發現樣例就過了,提交發現居然 \(50\%\)。然後發現 SPFA 死了。以下是 Luogu \(48\) 分程式碼(通過 \(50\%\)):

#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 等其他最短路演算法。那麼怎麼解決負權?

先把原序列切成幾段,每碰到一個 \(i=j_i\) 的就切一下。顯然段和段之間不影響。對於每一個段,我們再考慮分層。最後一個是第 \(0\) 層,其他的第 \(i\) 個就是 \(j_i\) 的層數再加一。

我們令第 \(i\) 層的初始權值為 \(-iK\)。這樣會發現,條件 \((1)\) 中的 \(i,j\) 必然隔一層,那麼 \(K\) 就消掉了。條件 \((2)\)\(i,j+1\) 割一層或兩層,兩層的話就不用考慮,否則 \(K\) 也消掉了。條件 \((3)\) 對於同一層的照樣做就行了。

於是原來的條件的權值只有 \(0\)

\(1\) 了,容易發現圖變成了 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;
}