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

图论建模

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

假设这条线路的开头为 \(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. MongoDB之用户管理

    注意点: 验证库: 建立用户时use到的库及用户的验证库,在使用用户时,要加上验证库才能登陆. 对于管理员用户,必须在admin下创建. 1. 建用户时,use到的库,就是此用户的验证库 2. 登录时 ...

  2. 揭秘UGO SQL审核功能4大特性,让业务平滑迁移至GaussDB

    业务挑战 数据库是企业应用系统的核心,SQL作为数据库查询.更新等操作的标准语言,重要性不言而喻.然而在实际的SQL开发过程中,也面临着诸多挑战: 数据库应用开发人员的SQL能力良莠不齐,经常写出不符 ...

  3. 使用 httputils + protostuff 实现高性能 rpc

    1.先讲讲 protostuf protostuf 一直是高性能序列化的代表之一.但是用起来,可难受了,你得先申明 protostuf 配置文件,并且要把这个配置文件转成类.所以必然要学习新语法.新工 ...

  4. Redis应用—1.在用户数据里的应用

    大纲 1.社区电商的业务闭环 2.Redis缓存架构的典型生产问题 3.用户数据在读多写少场景下的缓存设计 4.热门用户数据的缓存自动延期机制 5.缓存惊群与穿透问题的解决方案 6.缓存和数据库双写不 ...

  5. Typecho COS插件实现网站静态资源存储到COS,降低本地存储负载

    ** Typecho 简介** Typecho 是一个简单.强大的轻量级开源博客平台,用于建立个人独立博客.它具有高效的性能,支持多种文件格式,并具有对设备的响应式适配功能.Typecho 相对于其他 ...

  6. 2.mysql授权认证

    权限系统介绍 ● 什么是权限系统 权限系统是授予来自某个主机的某个用户可以查询.插入.修改.删除等数据库操作的权限 不能明确的指定拒接某个用户的连接 权限控制(授权与收回)的执行语句包括 create ...

  7. 【Java】获取近六个月的年月

    数据库里面存储的字段类型就是varchar,数据格式就是类似2024-12这样的年月格式. 目标: 以当前月份为标准,向前获取近6个月的年月(year_month)形成列表 // 获取近6个月的年月列 ...

  8. 【转载】 一次生产环境的NOHTTPRESPONSEEXCEPTION异常的排查记录

    https://www.freesion.com/article/41531004212/ 环境: jdk1.8+tomcat8+httpclient4.5.2 主要现象: 项目偶发出现org.apa ...

  9. Qt支持RKMPP硬解的视频监控系统/性能卓越界面精美/实时性好延迟低/录像存储和回放/云台控制

    一.前言 之前做的监控系统,已经实现了在windows上硬解码比如dxva2和d3d11va,后续又增加了linux上的硬解vdpau的支持,这几种方式都是跨系统的硬解实现方案,也是就是如果都是win ...

  10. Qt/C++音视频开发46-音视频同步保存到MP4

    一.前言 用ffmpeg单独做视频保存不难,单独做音频保存也不难,难的是音视频同步保存到MP4中,重点是音视频要同步,其实这也不难,只要播放那边音视频同步后的数据,写入到文件即可.最难的是在播放过程中 ...