[CC-SUBWAY]Subway Ride

题目大意:

一棵\(n(n\le5\times10^5)\)个点的含重边的树,总边数为\(m(m\le10^6)\),每条边有一个颜色。\(q(q\le5\times10^5)\)次询问,每次询问两个结点之间的路径(不经过重复结点)最多包含多少连续的同色段。

思路:

一个显然的结论是,对于连接相同两个点的重边,如果不同的颜色数量\(\le 3\),那么至少有一条边和路径两侧的颜色不相同,这时我们只需要发明一种新的颜色来代替这些颜色即可。因此每条边最多只需要保留\(2\)种颜色。

用\(f[i][0/1]\)表示到了第\(i\)个结点,上一条边是第\(0/1\)种颜色的答案。每次询问跑一遍这样的DP,单次的时间复杂度是\(\mathcal O(n)\)。

使用树上倍增的思想,\(f[i][j][k][l]\)表示\(i\sim anc[i][j]\)的路径上,第一条边是第\(k\)种颜色,最后一条边是第\(l\)种颜色,询问时将一整段转移即可。

时间复杂度\(\mathcal O(q\log n)\)。

源代码:

#include<map>
#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=5e5+1,logN=19;
int tot,anc[N][logN],dep[N],f[N][2],g[N][2];
int seg[N][logN][2][2],w[N][logN][2];
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
e[u].push_back(v);
e[v].push_back(u);
}
std::map<std::pair<int,int>,std::pair<int,int>> map;
inline int lg2(const float &x) {
return ((unsigned&)x>>23&255)-127;
}
inline void up(int &x,const int &y) {
x=std::max(x,y);
}
void dfs(const int &x,const int &par) {
if(par) {
int u=x,v=par;
if(u>v) std::swap(u,v);
const auto &p=map[std::make_pair(u,v)];
if(p.first) {
w[x][0][0]=p.first;
seg[x][0][0][0]=1;
}
if(p.second) {
w[x][0][1]=p.second;
seg[x][0][1][1]=1;
}
}
anc[x][0]=par;
dep[x]=dep[par]+1;
for(register int i=1;i<=lg2(dep[x]);i++) {
anc[x][i]=anc[anc[x][i-1]][i-1];
w[x][i][0]=w[anc[x][i-1]][i-1][0];
w[x][i][1]=w[anc[x][i-1]][i-1][1];
for(register int j=0;j<2;j++) {
if(!w[x][0][j]) continue;
for(register int k=0;k<2;k++) {
if(!w[x][i][k]) continue;
for(register int l=0;l<2;l++) {
if(!w[x][i-1][l]) continue;
for(register int m=0;m<2;m++) {
if(!w[anc[x][i-1]][0][m]) continue;
up(seg[x][i][j][k],seg[x][i-1][j][l]+seg[anc[x][i-1]][i-1][m][k]-(w[x][i-1][l]==w[anc[x][i-1]][0][m]));
}
}
}
}
}
for(auto &y:e[x]) {
if(y!=par) dfs(y,x);
}
}
inline int query(int u,int v) {
if(u==v) return 0;
if(dep[u]<dep[v]) std::swap(u,v);
f[u][0]=f[u][1]=g[u][0]=g[u][1]=0;
f[v][0]=f[v][1]=g[v][0]=g[v][1]=0;
for(register int i=lg2(dep[u]-dep[v]);i>=0;i--) {
const int &p=anc[u][i];
if(dep[p]<dep[v]) continue;
f[p][0]=f[p][1]=g[p][0]=g[p][1]=0;
for(register int j=0;j<2;j++) {
for(register int k=0;k<2;k++) {
if(!w[u][0][k]) continue;
for(register int l=0;l<2;l++) {
if(f[u][j]+seg[u][i][k][l]-(g[u][j]==w[u][0][k])>f[p][l]) {
f[p][l]=f[u][j]+seg[u][i][k][l]-(g[u][j]==w[u][0][k]);
g[p][l]=w[u][i][l];
}
}
}
}
u=p;
}
if(u==v) return std::max(f[u][0],f[u][1])-1;
for(register int i=lg2(dep[u]);i>=0;i--) {
const int &p=anc[u][i],&q=anc[v][i];
if(p==q) continue;
f[p][0]=f[p][1]=g[p][0]=g[p][1]=0;
for(register int j=0;j<2;j++) {
for(register int k=0;k<2;k++) {
if(!w[u][0][k]) continue;
for(register int l=0;l<2;l++) {
if(f[u][j]+seg[u][i][k][l]-(g[u][j]==w[u][0][k])>f[p][l]) {
f[p][l]=f[u][j]+seg[u][i][k][l]-(g[u][j]==w[u][0][k]);
g[p][l]=w[u][i][l];
}
}
}
}
f[q][0]=f[q][1]=g[q][0]=g[q][1]=0;
for(register int j=0;j<2;j++) {
for(register int k=0;k<2;k++) {
if(!w[v][0][k]) continue;
for(register int l=0;l<2;l++) {
if(f[v][j]+seg[v][i][k][l]-(g[v][j]==w[v][0][k])>f[q][l]) {
f[q][l]=f[v][j]+seg[v][i][k][l]-(g[v][j]==w[v][0][k]);
g[q][l]=w[v][i][l];
}
}
}
}
u=p;
v=q;
}
int p=anc[u][0];
f[p][0]=f[p][1]=g[p][0]=g[p][1]=0;
for(register int j=0;j<2;j++) {
for(register int k=0;k<2;k++) {
if(!w[u][0][k]) continue;
if(f[u][j]+seg[u][0][k][k]-(g[u][j]==w[u][0][k])>f[p][k]) {
f[p][k]=f[u][j]+seg[u][0][k][k]-(g[u][j]==w[u][0][k]);
g[p][k]=w[u][0][k];
}
}
}
p=0;
f[p][0]=f[p][1]=g[p][0]=g[p][1]=0;
for(register int j=0;j<2;j++) {
for(register int k=0;k<2;k++) {
if(!w[v][0][k]) continue;
if(f[v][j]+seg[v][0][k][k]-(g[v][j]==w[v][0][k])>f[p][k]) {
f[p][k]=f[v][j]+seg[v][0][k][k]-(g[v][j]==w[v][0][k]);
g[p][k]=w[v][0][k];
}
}
}
int ans=0;
for(register int i=0;i<2;i++) {
for(register int j=0;j<2;j++) {
up(ans,f[anc[u][0]][i]+f[0][j]-(g[anc[u][0]][i]==g[0][j]));
}
}
return ans-1;
}
int main() {
const int n=getint(),m=getint();
for(register int i=0;i<m;i++) {
int u=getint(),v=getint(),c=getint();
if(u>v) std::swap(u,v);
auto &p=map[std::make_pair(u,v)];
if(p.first<0) continue;
if(p.first==c||p.second==c) continue;
if(p.first==0) {
add_edge(u,v);
p.first=c;
continue;
}
if(p.second==0) {
p.second=c;
continue;
}
p.first=--tot;
p.second=0;
}
dfs(1,0);
const int q=getint();
for(register int i=0;i<q;i++) {
printf("%d\n",query(getint(),getint()));
}
return 0;
}

[CC-SUBWAY]Subway Ride的更多相关文章

  1. java设计模式学习笔记

    简介 设计模式可以分为五类 接口型 模式:适配器模式,外观模式,合成模式,桥接模式 职责型 模式:单例模式,观察者模式,调停者模式,代理模式,职责链模式,享元模式 构造型 模式:构建者模式,工厂方法模 ...

  2. C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析与解决方法

    对于C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析,目前本人分析两种情况,如下: 情况一: 借鉴麒麟.NET ...

  3. Android开发中常见的设计模式

    对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的 ...

  4. 基于Spring Boot的RESTful API实践(一)

    1. RESTful简述    REST是一种设计风格,是一组约束条件及原则,而遵循REST风格的架构就称为RESTful架构,资源是RESTful的核心,一个好的RESTful架构,通过URL就能很 ...

  5. android 开发设计模式---Strategy模式

    假设我们要出去旅游,而去旅游出行的方式有很多,有步行,有坐火车,有坐飞机等等.而如果不使用任何模式,我们的代码可能就是这样子的. 12345678910111213141516171819202122 ...

  6. Android开发中常见的设计模式(四)——策略模式

    策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变换. 假设我们要出去旅游,而去旅游出行的方式有很多,有步行,有坐火车,有坐飞机等等 ...

  7. Angular material mat-icon 资源参考_Maps

    ul,li>ol { margin-bottom: 0 } dt { font-weight: 700 } dd { margin: 0 1.5em 1.5em } img { height: ...

  8. Java_地铁购票系统

    定义了两个类,在Subway类中定义三个私有数据变量,线路号,经过站点,换乘站.以及4个方法分别实现从txt文件中导入线路信息:输出线路信息:查询两个站点经过站点数,并输出经过站点以及在某站换乘几号线 ...

  9. 2019.9.30极限测试 04.JAVA语言课堂测试试卷-极限测试

    题目存储在上传的文件当中. 代码实现 Subway 类: package ClassroomTest; public class Subway { private String railway; pr ...

  10. Thomson Plaza里面的三家店以及水果大会

    旅行应该是一个发现的过程,至少我是这么认为的.很多时候并不一定要到什么特别的地方,也可以感受到旅游的乐趣.我觉得只要能看到值得回味的东西就好了.而能回味的东西,往往是需要仔细地来品.像旅行社安排的那样 ...

随机推荐

  1. UNIX环境高级编程 第5章 标准I/O库

    本章是关于C语言标准I/O库的,之所以在UNIX类系统的编程中会介绍C语言标准库,主要是因为UNIX和C之间具有密不可分的关系.由于UNIX系统存在很多实现,而每个实现都有自己的标准I/O库,为了统一 ...

  2. [转]边框回归(Bounding Box Regression)详解

    https://blog.csdn.net/zijin0802034/article/details/77685438 Bounding-Box regression 最近一直看检测有关的Paper, ...

  3. mvn打war包以及解压包的方法

    有时候我们需要查看打成war包之后的目录,如果是maven项目我们可以直接用maven打包. 1.maven打包: 第一种: mvn package 如果不行先 mvn clean一下 第二种:(掌握 ...

  4. input 输入框 propertychange

    做搜索功能的时候,经常遇到输入框检查的需求,最常见的是即时搜索,今天好好小结一下. 即时搜索的方案: (1)change事件    触发事件必须满足两个条件: a)当前对象属性改变,并且是由键盘或鼠标 ...

  5. AS中一些不经常用到的快捷键

    1 书签 添加/移除书签 Ctrl+shift+F11 展示书签 shift+F11 下一个书签  shift+加号 上一个书签  shift+减号 2 折叠/展开代码块 展开代码块  ctrl+加号 ...

  6. 莫烦课程Batch Normalization 批标准化

    for i in range(N_HIDDEN): # build hidden layers and BN layers input_size = 1 if i == 0 else 10 fc = ...

  7. 聊天室(下篇)GatewayWorker 与 Laravel 的整合

    思路 上一篇大概梳理了一下 GatewayWorker 的基础知识.这篇就来准备整合 GatewayWorker 到 Laravel. GatewayWorker 是基于 Socket 监听的服务器框 ...

  8. Git的安装和使用(Linux)【转】

    转自:http://my.oschina.net/fhd/blog/354685 Git诞生于Linux平台并作为版本控制系统率先服务于Linux内核,因此在Linux上安装Git是非常方便的.可以通 ...

  9. C# 日文网址转punnycode

    Uri uri = new Uri(url); IdnMapping idn = new IdnMapping();url= url.Replace(uri.Host, idn.GetAscii(ur ...

  10. 读书笔记 effective c++ Item 52 如果你实现了placement new,你也要实现placement delete

    1. 调用普通版本的operator new抛出异常会发生什么? Placement new和placement delete不是C++动物园中最常遇到的猛兽,所以你不用担心你对它们不熟悉.当你像下面 ...