当前位置: 首页 > news >正文

图论好题推荐-逛公园

题目

在这里插入图片描述
在这里插入图片描述

题解

前置知识

学会使用 tarjan 算法找环,会使用树上倍增求两点间路径的编号最大值与最小值。

思路

pip_{i}piiii 作为题目中的 lll ,可以匹配到的最大 rrr 。因为 rrr 越大,包含的边和点就越多,所以 [i,i],[i,i+1],[i,pi−1],[i,pi][i,i],[i,i+1],[i,p_{i} -1],[i,p_{i} ][i,i],[i,i+1],[i,pi1],[i,pi] 作为 [l,r][l,r][l,r] 都是可以满足题意的。
下面考虑两个问题:

  1. 如何求 pip_{i}pi
  2. 如何利用 pip_{i}pi 求解答案?
如何求 $p_{i} $?

首先找环,可以遍历一棵 DFS 搜索树,如果出现返租边,就是出现了环。
对于每个环,求出环中点的编号的最大值(记为 max1max1max1)与最小值(记为 min1min1min1)。因为每次出现环是在出现返祖边的时候,所以求一下这条返祖边所连接的两个点之间路径上的点中的编号的最大值和最小值即可,可以用倍增优化,logloglog 级别。显然,如果某个区间 [l,r][l,r][l,r] 同时包含这个环的 min1min1min1max1max1max1,则该区间 [l,r][l,r][l,r] 一定不合法,反之,该区间 [l,r][l,r][l,r] 不会在这个环中不合法。
记 $pos_{i} $ 为 iii 作为环中编号最小点时,其对应的环中的编号最大点的编号最小值。因为同一个点有可能存在于多个环中(如图一),并且有可能在这些环中都是编号最小的点,所以要取对应的编号最大点的编号最小值。
这里的用处以后会知道。

图一。题目中只保证不存在一条边同时存在于两个不同的简单环,但可能有一个点在多个环中。

每次找到环时,posmin1=min(posmin1,max1)pos_{min1}=min(pos_{min1},max1)posmin1=min(posmin1,max1) 更新即可。
pip_{i}pi,考虑 j(i≤j≤n)j(i\le j\le n)j(ijn),jjj 带给 $p_{i} $ 的限制就是:$p_{i} < pos_{j} $,因为如果 $p_{i} $ 取到 posjpos_{j}posj 或者更大,就会同时包含 jjjposjpos_{j}posj,不合法。那么显然 pi=min⁡(posj−1)p_{i}=\min (pos_{j}-1)pi=min(posj1),求一个后缀最小值即可。

图二。结合样例理解 pospospos 数组和 ppp 数组含义(INF表示没有)。

编号12345678
pos3INFINF8INFINFINFINF
p27778888
如何利用 pip_{i}pi 求解答案?

可以暴力枚举 jjjxxxyyy,答案即为 ∑j=xy(min(y,pj)−j+1)\sum_{j=x}^{y}(min(y,p_{j})-j+1)j=xy(min(y,pj)j+1),时间复杂度O(nq)O(nq)O(nq)
考虑优化,显然可以看出,ppp 数组是单调不降的,故可以二分出在 xxxyyy 之间第一个p[]≥yp[ ]\ge yp[]y 的点,下标记为 kkk。对于任意 j(k≤j≤)j(k \le j \le )j(kj),有 y≤pjy\le p_{j}ypj,故 min(y,pj)=ymin(y,p_{j})=ymin(y,pj)=y,其贡献为 ∑j=ky(y−j+1)\sum_{j=k}^{y} (y-j+1)j=ky(yj+1),整理得 ∑j=ky(−j+1)+∑j=kyy\sum_{j=k}^{y} (-j+1)+\sum_{j=k}^{y} yj=ky(j+1)+j=kyy,可O(1)O(1)O(1)求出。
对于任意 KaTeX parse error: Undefined control sequence: \lej at position 4: j(x\̲l̲e̲j̲<k),有 min(y,pj)=pjmin(y,p_{j})=p_{j}min(y,pj)=pj。故其贡献为 ∑j=xk−1(pj−j+1)\sum_{j=x}^{k-1}(p_{j}-j+1)j=xk1(pjj+1),整理得 ∑j=xk−1pj+∑j=xk−1(−j+1)\sum_{j=x}^{k-1}p_{j}+\sum_{j=x}^{k-1}(-j+1)j=xk1pj+j=xk1(j+1),预处理前缀和,也可 O(1)O(1)O(1) 求出。
求解答案的总复杂度为 O(qlog⁡2n)O(q\log_{2}{n})O(qlog2n)

代码

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7,N=3e5+5;
bool_priority birdtjg=1;
int n,m,q;
vector<int> a[N];
int p[N],dfn[N],fmin_[N][30],fmax_[N][30],to[N][30],pos[N];
long long sp[N];      //注意开long long!
void dfs(int x,int fa)
{dfn[x]=dfn[fa]+1;  //dfn为深度fmin_[x][0]=min(fa,x),fmax_[x][0]=max(fa,x);to[x][0]=fa;for(int i=1;i<=20;i++) //倍增优化{to[x][i]=to[to[x][i-1]][i-1];fmin_[x][i]=min(fmin_[x][i-1],fmin_[to[x][i-1]][i-1]);fmax_[x][i]=max(fmax_[x][i-1],fmax_[to[x][i-1]][i-1]);}for(int i=0;i<a[x].size();i++){if(!dfn[a[x][i]])dfs(a[x][i],x);else if(dfn[a[x][i]]<dfn[x]&&a[x][i]!=fa) //出现返祖边(注意特判父亲!){int min1=1e9,max1=0,now=x;for(int j=20;j>=0;j--) //倍增求min1和max1{if(dfn[to[now][j]]>=dfn[a[x][i]]){min1=min(min1,fmin_[now][j]);max1=max(max1,fmax_[now][j]);now=to[now][j];}}pos[min1]=min(pos[min1],max1);  //求pos[]}}
}
int main()
{freopen("graph.in","r",stdin);freopen("graph.out","w",stdout);memset(pos,127,sizeof(pos));  //因为要取min,所以要赋值为INFscanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);a[u].push_back(v);   a[v].push_back(u);}dfs(1,0);    //预处理int hmin=1e9;       for(int i=n;i>=1;i--) //从n到1遍历方便求后缀最小值{hmin=min(hmin,pos[i]-1);//因为pos[i]肯定是小于等于n的,如果不是,就意味着该点没有出现在某个环的min1if(hmin>n) p[i]=n;     //如果后缀最小值大于n,说明没有限制,赋值为nelse p[i]=hmin;}for(int i=1;i<=n;i++)  //前缀和sp[i]=sp[i-1]+p[i];scanf("%d",&q);while(q--){int x,y;scanf("%d%d",&x,&y);	int l=x,r=y,k=0;   //肯定能够二分出至少一个p[j]>=ylong long ans=0;  //注意开long long!while(l<=r){int mid=(l+r)/2;if(p[mid]>=y) k=mid,r=mid-1;else l=mid+1;}ans=1ll*(y-k+2)*(y-k+1)/2;  //求>=k的贡献ans+=sp[k-1]-sp[x-1]-1ll*(k-x)*(x+k-3)/2; //求<k的贡献//求解的过程中可能爆int,注意用1ll*printf("%lld\n",ans);}return 0;
}

小细节

  • 前缀和数组和答案都需要开long long。求解的过程中也有可能爆 int,注意用1ll*(详见注释)。
  • 题目中说不存在一条边同时存在于两个不同的简单环,所以求环中点的 min1min1min1max1max1max1 的时候,直接暴力枚举整个环即可,每条边最多被遍历一次,O(m)O(m)O(m) 复杂度下即可求解。 我贴的代码是倍增的,时间复杂度反而更大了。
http://www.xdnf.cn/news/1382923.html

相关文章:

  • 【LInux】常用命令笔记
  • ArkUI框架之Canvas 画布
  • 什么是最小二乘法
  • 二、开关电源的EMC改善措施
  • CVPR2025丨VL2Lite:如何将巨型VLM的“知识”精炼后灌入轻量网络?这项蒸馏技术实现了任务专用的极致压缩
  • 虚幻基础:角色变换角色视角蒙太奇运动
  • 基于SpringBoot的老年人健康数据远程监控管理系统【2026最新】
  • 嵌入式开发学习———Qt软件环境下的C++学习(七)
  • 图论基础篇
  • Mybatis中缓存机制的理解以及优缺点
  • 微服务相关面试题
  • stable-baseline3介绍
  • 个人博客运行3个月记录
  • mac m4执行nvm install 14.19.1报错,安装低版本node报错解决
  • 【STM32】G030单片机的窗口看门狗
  • Flutter:ios打包ipa,证书申请,Xcode打包,完整流程
  • LeetCode Hot 100 第7天
  • mac系统本地部署Dify步骤梳理
  • 仓颉编程语言青少年基础教程:输入输出
  • 模拟实现Linux中的进度条
  • [Mysql数据库] 知识点总结5
  • 天津医科大学肿瘤医院冷热源群控系统调试完成:以 “精准控温 + 高效节能” 守护医疗核心场景
  • 实战演练(一):从零构建一个功能完备的Todo List应用
  • Spring事务管理机制深度解析:从JDBC基础到Spring高级实现
  • 力扣(LeetCode) ——965. 单值二叉树(C语言)
  • C#写的一键自动测灯带的应用 AI帮写的。
  • [灵动微电子 MM32BIN560CN MM32SPIN0280]读懂电机MCU之串口DMA
  • list 手动实现 1
  • 学习日志40 python
  • 微服务即时通信系统(十三)--- 项目部署