首页 > 其他分享 >Luogu P1983 车站分级 题解 [ 绿 ] [ 拓扑排序 ] [ 图论建模 ] [ 虚点 ]

Luogu P1983 车站分级 题解 [ 绿 ] [ 拓扑排序 ] [ 图论建模 ] [ 虚点 ]

时间:2024-07-30 21:17:34浏览次数:15  
标签:int 题解 边权 st 3005 P1983 虚点 Luogu 拓扑

车站分级:很好的拓扑排序题,细节有点多。

图论建模

首先观察对于一条线路,我们可以从中直接得到什么信息:

假设这条线路的开头为 \(st\) ,结尾为 \(ed\) ,那么在 \([st,ed]\) 的车站中,没有被选入线路的点一定比选入线路的点的 级数 至少 少 \(1\) 。

对于这一点条件,我们就可以建模了。

和差分约束一样,我们从某个点起连到所有比其多 \(1\) 的点的有向边,边权赋为 \(1\) 。

然后拓扑排序跑最长路即可。不是最短路的原因是 这些都是充分必要条件,每一个都必须满足,因此要选最长路。

这样建图是 \(O(n^2m)\) 的,虽然常数小,但还是容易被卡。

优化

发现复杂度瓶颈在于是一堆点往一堆点连边,所以我们从优化建边的方式入手:虚点优化。

我们把那些原来的起点们连多条向这个虚点的有向边,边权赋为 \(0\) ;然后从这个虚点连多条向原来的终点们的有向边,边权赋为 \(1\) ,这样就可以在 \(O(n)\) 的时间里完成 \(O(n^2)\) 的建边了。

总体复杂度是 \(O(nm)\) 。

细节

边权全部赋值为 \(1\) 的做法

对于这种做法,非常万能,但不是常人的思维。

写这种做法细节很少,我们无需处理起点是 \(0\) 是 \(1\) ,直接全赋为 \(0\) 就好了,因为我们先不考虑起点。

最后统计答案的时候,我们只需要将 $ ans / 2 +1$ 即可, \(/2\) 是因为经过虚点一次会走两条边,要除回来;\(+1\) 是因为要加上起点。

同时注意有可能虚点入度为 \(0\) ,所以虚点也要加入拓扑排序的初始化中,比如下面的数据:

in:
3 1
2 1 2

out:
1

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[1005],dp[3005],rd[3005],ans=0;
vector<int>g[3005];
queue<int>q;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int s,st=0,ed=0;
		cin>>s;
		bitset<1005>isin;
		for(int j=1;j<=s;j++)
		{
			cin>>a[j];
			isin[a[j]]=1;
			if(j==1)st=a[j];
			if(j==s)ed=a[j];
		}
		int vt=n+i;
		for(int j=st;j<=ed;j++)
		{
			if(isin[j]==0)
			{
				g[j].push_back(vt);
				rd[vt]++;
			}
			else
			{
				g[vt].push_back(j);
				rd[j]++;
			}
		}
	}
	for(int i=1;i<=n+m;i++)
	{
		if(rd[i]==0)
		{
			q.push(i);
			dp[i]=0;
		}
	}
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(auto v:g[u])
		{
			dp[v]=max(dp[v],dp[u]+1);
			rd[v]--;
			if(rd[v]==0)
			{
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n+m;i++)
	{
		ans=max(ans,dp[i]/2+1);
	}
	cout<<ans;
	return 0;
}

对于上面那种边权先赋为 \(1\) ,后面再赋为 \(0\) 的做法

依然是上面的那个 hack 数据:

in:
3 1
2 1 2

out:
1

如果把入度为 \(0\) 的虚点的最长路也初始化为 \(1\) 的话,就会导致这组数据结果多一个 \(1\)。因此应该把虚点初始化为 \(0\) ,其他点初始化为 \(1\) 。

另外,不要读错题了,只有起点和终点之间的车站才受分级的约束,而不是全部车站。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[1005],dp[3005],rd[3005],ans=0;
struct edge{
	int to,w;
};
vector<edge>g[3005];
queue<int>q;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int s,st=0,ed=0;
		cin>>s;
		bitset<1005>isin;
		for(int j=1;j<=s;j++)
		{
			cin>>a[j];
			isin[a[j]]=1;
			if(j==1)st=a[j];
			if(j==s)ed=a[j];
		}
		int vt=n+i;
		for(int j=st;j<=ed;j++)
		{
			if(isin[j]==0)
			{
				g[j].push_back({vt,0});
				rd[vt]++;
			}
			else
			{
				g[vt].push_back({j,1});
				rd[j]++;
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(rd[i]==0)
		{
			q.push(i);
			dp[i]=1;
		}
	}
	for(int i=n+1;i<=n+m;i++)
	{
		if(rd[i]==0)
		{
			q.push(i);
			dp[i]=0;
		}
	}
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(auto tmp:g[u])
		{
			int v=tmp.to,w=tmp.w;
			dp[v]=max(dp[v],dp[u]+w);
			rd[v]--;
			if(rd[v]==0)
			{
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n+m;i++)
	{
		ans=max(ans,dp[i]);
	}
	cout<<ans;
	return 0;
}
/*
3 1
2 1 2
*/

标签:int,题解,边权,st,3005,P1983,虚点,Luogu,拓扑
From: https://www.cnblogs.com/zhr0102/p/18333363

相关文章

  • CF538H Summer Dichotomy 题解
    Description有\(T\)名学生,你要从中选出至少\(t\)人,并将选出的人分成两组,可以有某一组是空的。有\(n\)名老师,每名老师要被分配到两个小组之一,对于第\(i\)名老师,要求所在的小组中的学生人数\(\in[l_i,r_i]\)。此外,有\(m\)对老师不能在同一个小组中。你需要判断能否......
  • SP8099 TABLE - Crash´s number table 题解
    题目传送门前置知识一般的积性函数|数论分块|莫比乌斯反演解法令\(n\lem\)。考虑莫比乌斯反演,推式子,有\(\begin{aligned}&\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\operatorname{lcm}(i,j)\\&=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\frac{ij}{\gcd(i,j)......
  • 题解:Pinely Round 4 (Div. 1 + Div. 2) A
    A.MaximizetheLastElementtimelimitpertest:1secondmemorylimitpertest:256megabytesinput:standardinputoutput:standardoutputYouaregivenanarray\(a\)of\(n\)integers,where\(n\)isodd.Inoneoperation,youwillremovetwoa......
  • P4449 于神之怒加强版 (题解)
    题目链接P4449于神之怒加强版题目大意:求\[\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)^k\]\(数据范围n,m\leq5e6\)\(二话不说,先开导式子(假定n<m):\)\begin{aligned}ans&=\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)^k\\\end{aligned}\[先套路地枚举d=gcd(i,j)\]\begin{align......
  • 题解 - Alice
    题目描述Alice和Bob在玩游戏,游戏规则如下:有两堆石子,每堆石子有非负整数个Alice与Bob轮流操作,Alice先手,每次可以从任意一堆石子中取出若干个取出的石子个数应满足为另一堆石子个数的因数(此处规定任何数均为0的因数)将石子取完的玩家获胜给定一个初始状态,现......
  • [POI2007] OSI-Axes of Symmetry 题解
    给出一个多边形,求对称轴数量。考虑对于一个多边形,其是对称的当且仅当对于若干个边(角),其左右的角与边都是对称的。考虑如果我们对于内角构造出一种单射,映射为一个整数,将边映射为它的边长,那么我们按照角,边,角,边,……的顺序将他们加入数组中,能构造出一个长度为\(2n\)的数组,将这个......
  • 旅行 题解
    题目id:20300题目描述鱼大大计划环华夏自驾游游玩一圈,在他的计划中,这次旅行将在\(m\)天内完成,预计游玩\(n\)个城市,每个城市游玩\(x\)天,然后赶路开车以均速去到下一个城市,现鱼大大从海洋城市出发,按游玩顺序先后给出每个城市和上一个城市之间的距离(\(km\)),问鱼大大每天至少赶路多......
  • 质数差列 题解
    题目id:20315题目描述驰骋宇宙的鱼大大找到了一个古遗迹,稍作研究后发现这是一个来着远古的质数星球文明遗迹,这个文明的特点是所有事物都和质数息息相关。于是,鱼大大赶紧列出了一堆的质数,以方便自己的研究。这天鱼大大找到了质数星球文明的一个遗迹仓库大门,正准备破解密码的同时......
  • [POI2008] POC-Trains 题解
    前言题目链接:洛谷。时间复杂度和输入同阶的做法。题意简述有\(n\)(\(n\leq10^3\))个长\(m\)的字符串,\(q\)(\(q\leq10^5\))次操作,交换两个字符串的两个字符。问每个字符串在所有时刻,最多有几个和它相等。题目分析套路做法看到字符串相等,想到使用哈希。但是要支持修改,怎么......
  • 题解:[ABC363D] Palindromic Number
    提示特定位数的回文数数量是可以快速计算出来的,对于特定的位数\(n\),第一位由于不能有前导\(0\),共有\(9\)种选择,而从第\(2\)位到第\(\frac{n+1}{2}\)位都有\(10\)种选择(一个回文数完全由它的前半部分确定)。所以\(n\)位数中共有\(S(n)=9*10^\frac{n-1}{2}......