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

图论建模

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

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

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

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

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

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

优化

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

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

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

细节

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

对于这种做法,非常万能,但我想不到。

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

最后统计答案的时候,我们只需要将 $ ans \div 2 +1$ 即可,\(\div2\) 是因为经过虚点一次会走两条边,要除回来;\(+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;
}

Luogu P1983 车站分级 题解 [ 绿 ] [ 拓扑排序 ] [ 图论建模 ] [ 虚点 ]的更多相关文章

  1. 洛谷P1983车站分级题解

    题目 这个题非常毒瘤,只要还是体现在其思维难度上,因为要停留的车站的等级一定要大于不停留的车站的等级,因此我们可以从不停留的车站向停留的车站进行连边,然后从入度为0的点即不停留的点全都入队,然后拓扑排 ...

  2. 【luogu P1983 车站分级】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1983 符合了NOIP命题的特点,知识点不难,思维量是有的. step1:把题读进去,理解.得到 非停靠点的等 ...

  3. Luogu P1983 车站分级

    (一周没写过随笔了) 这道题有坑! 看到题目,发现这么明显(??)的要求顺序,还有什么想法,拓扑! 将每条路范围内等级大于等于它的点(不能重复(坑点1))和它连一条边,注意起点终点都要有(坑点2),然 ...

  4. 洛谷P1983 车站分级

    P1983 车站分级 297通过 1.1K提交 题目提供者该用户不存在 标签图论贪心NOIp普及组2013 难度普及/提高- 提交该题 讨论 题解 记录 最新讨论 求帮忙指出问题! 我这么和(diao ...

  5. 洛谷P1983车站分级

    洛谷\(P1983\)车站分级(拓扑排序) 目录 题目描述 题目分析 思路分析 代码实现 题目描述 题目在洛谷\(P1983\)上 ​ 题目: 一条单向的铁路线上,依次有编号为 \(1, 2, -, ...

  6. [LOJ 3101] [Luogu 5332] [JSOI2019]精准预测(2-SAT+拓扑排序+bitset)

    [LOJ 3101] [Luogu 5332] [JSOI2019]精准预测(2-SAT+拓扑排序+bitset) 题面 题面较长,略 分析 首先,发现火星人只有死和活两种状态,考虑2-SAT 建图 ...

  7. NOIP 车站分级 (luogu 1983 & codevs 3294 & vijos 1851) - 拓扑排序 - bitset

    描述 一条单向的铁路线上,依次有编号为 1, 2, ..., n 的 n 个火车站.每个火车站都有一个级别,最低为 1 级.现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车 ...

  8. 【基础练习】【拓扑排序】codevs3294 车站分级题解

    题目来源:NOIP2013 普及第四题 题目描写叙述 Description 一条单向的铁路线上,依次有编号为1, 2, -, n的n个火车站.每一个火车站都有一个级别,最低为1级.现有若干趟车次在这 ...

  9. P1983 车站分级 思维+拓扑排序

    很久以前的一道暑假集训的题,忘了补. 感觉就是思维建图,加拓扑排序. 未停靠的火车站,必然比停靠的火车站等级低,就可以以此来建边,此处注意用vis来维护一下,一个起点和终点只建立一条边,因为不这样的话 ...

  10. P1983 车站分级[拓扑]

    题目描述 一条单向的铁路线上,依次有编号为 1, 2, -, n1,2,-,n的 nn个火车站.每个火车站都有一个级别,最低为 11 级.现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟 ...

随机推荐

  1. Git之清除历史记录操作

    近期公司需要将之前代码仓库中的提交记录都清理,所以操作一下,记录一下步骤: 安全考虑: 有时候在提交代码时,不小心提交了敏感数据,如账号密码什么的,这样在历史记录中就可以查看到,这样很不安全,所以就需 ...

  2. Codeforces Round 892 (Div.2)

    A. United We Stand 题解 赛时想复杂了 题目要求我们保证数组\(c\)中的数不是数组\(b\)中任意一个数的因子 我们考虑将最小值置于数组\(b\)即可 const int N = ...

  3. SSD:清华出品,可切换密集稀疏的大模型预训练加速方案 | ICML'24

    来源:晓飞的算法工程笔记 公众号,转载请注明出处 论文: Exploring the Benefit of Activation Sparsity in Pre-training 论文地址:https ...

  4. 设计模式:可复用面向对象软件的基础 pdf电子书分享

    <设计模式:可复用面向对象软件的基础>是引导读者走出软件设计迷宫的指路明灯,凝聚了软件开发界几十年设计经验的结晶.四位顶尖的面向对象领域专家精心选取了最具价值的设计实践,加以分类整理和命名 ...

  5. BitLocker驱动器加锁和解锁

    应用场景: 单位配备给你使用的电脑(Win10系统),偶尔也会有其他人使用.你可以设置某一个磁盘为你的私密数据存储空间,只有你输入密码后才能进入磁盘.即使系统重装.硬盘被拆下来挂载到其他机器上,没有密 ...

  6. IDEA 一个服务同时启动多个实例

    有些时候为了测试,服务的分发,我们可以在IDEA中对一个微服务启动多个实例,配置方法如下: 1.编辑同时启动的微服务. 勾选 allow parallel run 2.编辑完成后复制一份配置. 3.复 ...

  7. 聊一下怎么十分钟速水CVE

    题目是真的.今天晚上刚接触关于CVE的审计和提交.只能说:牛逼的是真牛逼,水的是真水. 我接下来教你,怎么在10分钟里找到一个CVE的中危漏洞并且提交.然后你就可以去咸鱼接单了,一个一铂快 打开htt ...

  8. vue composition api 访问 原vue2中 this.$refs

    1 <el-form class="user-form" ref="ruleFormRef" :model="userForm" st ...

  9. 前端每日一知之web攻击方式

    脑图在线链接 本文内容依据[js每日一题]公众号精彩文章总结而来

  10. day02 计算机组成

    day02 计算机组成 1.硬件 计算机硬件是指一些物理装置按照系统结构的要求构成一个有机整体为计算机软件运行提供物质基础 构成最基础的硬件有: CPU 内存 主板 IO设备 2.软件 计算机软件可以 ...