【洛谷5292】[HNOI2019] 校园旅行(思维DP)
大致题意: 给你一张无向图,每个点权值为\(0\)或\(1\),多组询问两点之间是否存在一条回文路径。
暴力\(DP\)
首先,看到\(n\)如此之小(\(n\le5000\)),便容易想到一个\(O(m^2)\)的暴力\(DP\)。
我们用\(f_{i,j}\)表示\(i\)与\(j\)两点之间是否存在一条回文路径。
初始化,\(f_{i,i}=1,f_{i,j}=1(s_i=s_j)\),即分别预处理最短的奇数长度回文路径和偶数长度回文路径。
然后我们把所有\(f_{i,j}=1\)的点对\((i,j)\)扔入一个队列里,用类似于\(BFS\)的方式,每次枚举\(i\)的一个相邻节点\(x\)与\(j\)的一个相邻节点\(y\),如果\(s_x=s_y\),则显然存在一条回文路径\(x->i->j->y\),因此更新\(f_{x,y}=1\)并将\((x,y)\)扔入队列里。
这里要加上一个很显然的优化,即如果\(f_{x,y}\)原本就为\(1\),我们就不进行操作。
这样每组点对最多被枚举一次,这里的时间复杂度是\(O(n^2)\)。
但枚举相邻节点时要同时枚举两条边,因此复杂度就变成了\(O(m^2)\)。
不难发现,这个算法时间复杂度的瓶颈就在于枚举两条边这里,因此我们需要考虑对这个地方进行优化。
奇偶性与二分图性质
我们先考虑只在同色点之间连边。
考虑到每条边可以重复多次,也就是说,我们在转移时如果在一条边上无限走,则可以无限刷长度。
但是,我们无限刷长度不一定能改变奇偶性。
不过,我们至少可以得出一个结论,在\(DP\)转移时,只要是奇偶性相同的一段同色路径,我们就可以进行转移。
那么什么时候奇偶性不同也可以转移呢?
这时候就要借助二分图的定义来求解了。
考虑先判断当前图是否是二分图,这只需要\(DFS\)给相邻点染不同颜色,出现矛盾就说明不是二分图,否则是二分图。
而二分图有个性质,即可以将图中点集划分成两部分,其中同一部分的点之间没有边。
也就是说,从一个点出发,必然要沿着这一个点集\(->\)另一个点集\(->\)这一个点集\(->\)另一个点集\(->...->\)这一个点集这样的路径走才能走回到该点,则经过边数为偶数,无法改变奇偶性。
否则,图中必然存在奇环,而通过奇环就可以改变奇偶性了。
大力删边
所以我们前面讲了这么多是要干什么呢?就是为了删掉图中的一些边,使边数变成\(O(n)\)规模。
我们要明白二分图的另一个性质,即二分图的一棵生成树也满足二分图性质,无法改变奇偶性。
因此,对于每一个是二分图的同色连通块,我们就可以只保留一棵生成树。
而对于不是二分图的连通块,其实我们也可以先取一棵生成树,然后只要给这张图中任意一个节点加上一个自环,这样也可以改变奇偶性,与原连通块是等价的。
而对于只在异色点之间连边,也有类似的规律,而且我们可以发现它必定是二分图(将点集按颜色划分成两个点集),可以直接保留生成树。
于是点一下就少了很多,可以直接按前面的暴力\(DP\)来搞了!
这时边数是\(O(n)\),所以时间复杂度也就是\(O(n^2)\)。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 5000
#define M 500000
#define mp make_pair
#define fir first
#define sec second
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,ee,H,T,lnk[N+5],f[N+5][N+5];string s;
struct edge {int to,nxt;}e[(M<<1)+5];
typedef pair<int,int> Pr;Pr q[N*N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
I void reads(string& x) {x="";W(isspace(c=tc()));W(x+=c,!isspace(c=tc())&&~c);}
}F;
class GraphBuilder//建新图
{
private:
#define nadd(x,y) (ne[++nee].nxt=nlnk[x],ne[nlnk[x]=nee].to=y)//建新边
int nee,t,col[N+5];
I void Travel(CI x,CI op)//扫一遍连通块(op表示只能在同色/异色点间连边),建好生成树,同时判断是否为二分图
{
for(RI i=lnk[x];i;i=e[i].nxt) (s[x-1]==s[e[i].to-1])==op&&
(
col[e[i].to]?(col[x]==col[e[i].to]&&(t=0))//如果产生矛盾说明不是二分图
:(col[e[i].to]=3-col[x],Travel(e[i].to,op),nadd(x,e[i].to),nadd(e[i].to,x))//给相邻点染上不同颜色
);
}
public:
int nlnk[N+5];edge ne[(M<<1)+5];
I void Build()//建图
{
#define Clear() for(i=1;i<=n;++i) col[i]=0
RI i;Clear();for(i=1;i<=n;++i) !col[i]&&(col[i]=t=1,Travel(i,1),!t&&nadd(i,i));//对于非二分图建一个自环
Clear();for(i=1;i<=n;++i) !col[i]&&(col[i]=1,Travel(i,0),0);
}
}G;
#define Push(x,y) (q[++T]=mp(x,y),f[x][y]=f[y][x]=1)
I void DP()//动态规划
{
RI i,j;Pr k;W(H<=T) for(i=G.nlnk[(k=q[H++]).fir];i;i=G.ne[i].nxt)//枚第一个点的相邻节点
for(j=G.nlnk[k.sec];j;j=G.ne[j].nxt) s[G.ne[i].to-1]==s[G.ne[j].to-1]&&//枚第二个点的相邻节点
!f[G.ne[i].to][G.ne[j].to]&&Push(G.ne[i].to,G.ne[j].to);//扔入队列中
}
int main()
{
RI Qtot,i,x,y;for(F.read(n,m,Qtot),F.reads(s),i=1;i<=m;++i)//读入数据
F.read(x,y),add(x,y),add(y,x),s[x-1]==s[y-1]&&Push(x,y);//初始化队列
for(G.Build(),i=1;i<=n;++i) Push(i,i);DP();//建新图,初始化队列,然后DP
W(Qtot--) F.read(x,y),puts(f[x][y]?"YES":"NO");return 0;//对于每个询问输出答案
}
【洛谷5292】[HNOI2019] 校园旅行(思维DP)的更多相关文章
- 洛谷P5292 [HNOI2019]校园旅行(二分图+最短路)
题面 传送门 题解 如果暴力的话,我们可以把所有的二元组全都扔进一个队列里,然后每次往两边更新同色点,这样的话复杂度是\(O(m^2)\) 怎么优化呢? 对于一个同色联通块,如果它是一个二分图,我们只 ...
- DP【洛谷P2134】 百日旅行
[洛谷P2134] 百日旅行 题目背景 重要的不是去哪里,而是和你在一起.--小红 对小明和小红来说,2014年7月29日是一个美好的日子.这一天是他们相识100天的纪念日. (小明:小红,感谢你2场 ...
- 【BZOJ5492】[HNOI2019]校园旅行(bfs)
[HNOI2019]校园旅行(bfs) 题面 洛谷 题解 首先考虑暴力做法怎么做. 把所有可行的二元组全部丢进队列里,每次两个点分别向两侧拓展一个同色点,然后更新可行的情况. 这样子的复杂度是\(O( ...
- [HNOI2019]校园旅行(构造+生成树+动规)
题目 [HNOI2019]校园旅行 做法 最朴素的做法就是点对扩展\(O(m^2)\) 发现\(n\)比较小,我们是否能从\(n\)下手减少边数呢?是肯定的 单独看一个颜色的联通块,如果是二分图,我们 ...
- 洛谷P1027 Car的旅行路线
洛谷P1027 Car的旅行路线 题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速 ...
- 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)
洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...
- 洛谷 P3177 [HAOI2015]树上染色 树形DP
洛谷 P3177 [HAOI2015]树上染色 树形DP 题目描述 有一棵点数为 \(n\) 的树,树边有边权.给你一个在 \(0 \sim n\)之内的正整数 \(k\) ,你要在这棵树中选择 \( ...
- 洛谷 P4072 [SDOI2016]征途 斜率优化DP
洛谷 P4072 [SDOI2016]征途 斜率优化DP 题目描述 \(Pine\) 开始了从 \(S\) 地到 \(T\) 地的征途. 从\(S\)地到\(T\)地的路可以划分成 \(n\) 段,相 ...
- 洛谷P1880 石子合并(区间DP)(环形DP)
To 洛谷.1880 石子合并 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1 ...
- 洛谷P1063 能量项链(区间DP)(环形DP)
To 洛谷.1063 能量项链 题目描述 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的 ...
随机推荐
- offset、client、scroll、screen的自己理解
body是DOM对象里的body子节点,即 <body> 标签: documentElement 是整个节点树的根节点root,即<html> 标签: window.scree ...
- cmake指令详解
所需文件名:CmakeLists.txt,在需要操作的每个目录下都需要 PROJECT(工程名字) 这条指令会自动创建两个变量: <projectname>_BINARY_DIR( ...
- mysql 命令笔记
添加密码 mysqladmin -uroot -p password 123456 创建用户只能在10.0.0.0网段下访问数据库grant select,create,insert,update o ...
- 97 条 Linux 运维工程师常用命令总结[转]
1.ls [选项] [目录名 | 列出相关目录下的所有目录和文件 -a 列出包括.a开头的隐藏文件的所有文件 -A 通-a,但不列出"."和".." -l 列出 ...
- (转)DB2 db2diag.log 日志分析
DB2 db2diag.log 日志分析 原文:http://blog.csdn.net/lyjiau/article/details/52129997 db2diag.log是用来记录DB2数据库运 ...
- express遇到的问题
1. 如何引入express? cnpm install express --save 其中--save可以保存到依赖项中. 接着 var express = require("expres ...
- connection reset by peer, socket write error问题排查
2018-03-15更新:弄明白connection reset产生的原因,见重新分析connection reset by peer, socket write error错误原因 在开发文件上传功 ...
- java Folder transform to Source Folder
右键文件夹然后选择Build Path ===>Use as Source Folder 里面的东西现在就可以编译了 然后想要让一个源码包变成一个文件夹的话: 只需要再次右键源码包==>选 ...
- Jersey统一异常处理
众所周知,java服务提供者提供给服务请求者应该是特定格式的数据,而不能出现异常栈类似信息,那么jersey中,如何添加统一的异常处理呢? 针对jersey启动如果是实现了ResourceConfig ...
- C# 类型、对象、线程栈和托管堆在运行时的关系
我们将讨论类型.对象.线程栈和托管堆在运行时的相互关系,假定有以下两个类定义: internal class Employee { public int GetYearsEmplo ...