题目链接:P3629 [APIO2010]巡逻


首先看题,从1号结点开始,全部遍历并回到1号结点会恰好经过所有的边两次,这样总长度为 2∗(n−1)2*(n-1)2∗(n−1)。
那么如果建立一条路以后,由题意可知新道路必须经过恰好一次,也只能经过一次,所以新路从x到y形成一个环,这样就可以用新路的权值1来代替老路径中从x到y的路径1次,也就是说从x到y之间的路径在建立了新路以后只需要再经过一次就行了。所以我们要求最短,肯定要把整棵树上的最长链也就是树的直径省掉答案最优。所以当k = 1 时,我们找到最长链,在这两个端点之间加一条新路,设直径为L,则答案ans=2∗(n−1)−L+1ans = 2*(n-1) - L +1ans=2∗(n−1)−L+1(+1是因为要走一次新加的新路)。

当k = 2时,我们按照刚刚的理论,再求一次最长链。这样形成两个环,第一条肯定不能跟第二条一样,所以我们求完第一条以后要把第一条最长链给删去。这里找到第一次的最长链,遍历一遍,将值从1变成-1。
如果第二条与第一条链没有重叠,那么就跟第一条链一样,直接减去即可。
若第二条连与第一条链有重叠,那么由于本应只走一次的路径会走两次,多减去的值需要加回来,但是由于第一条链上的所有边已经取反为-1,这样减去负数又相当于加上这个数,所以两种答案是一样的。
最终的答案为ans=2∗(n−1)−(L1−1)−(L2−1)=2∗n−L1−L2ans = 2*(n-1) - (L_1-1) - (L_2-1) = 2*n - L_1 - L_2ans=2∗(n−1)−(L1​−1)−(L2​−1)=2∗n−L1​−L2​
总时间复杂度为O(n)O(n)O(n)

这里将第一条链的值全部赋值为-1的时候用到了一个位运算的小技巧 —— 成对变换。
要注意成对变换的时候要初始化tot = 1

当n为偶数的时候,n XOR 1 = n + 1 当n为奇数的时候,n XOR 1 = n - 1

利用这个小技巧,我们在使用链式前向星的时候初始化tot等于1,这样每天无向边看成的两条有向边会成对存储在ver和edge数组的下标为2和3,4和5…的位置上,通过对下标进行XOR1运算,就可以直接定位到当前边的反向的边。换句话说,如果ver[i]是第i条边的终点,那么ver[i xor1]就是第i条边的起点。
——《算法竞赛进阶指南》

放到代码里就是这样:

 while(fa[q]){//成对变换edge[fa[q]] = edge[fa[q]^1] = -1;q = ver[fa[q]^1];}

这里也可以tot = 0,我们直接把head数组置为-1,然后tot从0开始,因为成对变换0和1,2和3都是成立的。如果这样的话,我们就需要在边界上注意一下。
放到代码里就是这样:

fa[p] = -1;while(~fa[q]){//成对变换edge[fa[q]] = edge[fa[q]^1] = -1;q = ver[fa[q]^1];}

具体的看下面的代码就好

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<bitset>
#include<vector>
#include<queue>
#define ls (p<<1)
#define rs (p<<1|1)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
#define lowbit(p) p&(-p)
using namespace std;typedef long long ll;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1e5+7;
const int M = 2007;
int n,k;
int ver[N << 1],edge[N << 1],nex[N << 1],head[N],tot = 1;
int fa[N];
bool vis[N];
int dis[N];void add(int u,int v,int val){ver[++tot] = v;edge[tot] = val;nex[tot] = head[u];head[u] = tot;
}void dfs(int x,int &t){//不能处理负值vis[x] = 1;for(int i = head[x];i;i = nex[i]){int y = ver[i],z = edge[i];if(vis[y])continue;dis[y] = dis[x] + z;if(dis[y] >= dis[t])t = y;fa[y] = i;//存路径,用于成对变换,存的是前向星的指针dfs(y,t);}vis[x] = 0;//回溯
}void DP(int x,int &t){//可以处理负值vis[x] = 1;for(int i= head[x];i;i = nex[i]){int y = ver[i],z = edge[i];if(vis[y])continue;DP(y,t);t = max(t,dis[x] + dis[y] + z);dis[x] = max(dis[x],dis[y] + z);}
}int main()
{scanf("%d%d",&n,&k);over(i,1,n-1){int x,y;scanf("%d%d",&x,&y);add(x,y,1);add(y,x,1);}int p = 1;//两次dfs找最远的p和qdfs(1,p);dis[p] = fa[p] = 0;//找到之后把它当成根节点int q = p;dfs(p,q);int ans = ((n - 1) * 2) - dis[q] + 1;// + 1是因为新增了一条必须走一次的边if(k == 2){while(fa[q]){//成对变换edge[fa[q]] = edge[fa[q]^1] = -1;q = ver[fa[q]^1];}q = 0;memset(dis,0,sizeof dis);DP(p,q);ans -= q - 1;}printf("%d\n",ans);return 0;
}

另一种初始化的成对变换

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<bitset>
#include<vector>
#include<queue>
#define ls (p<<1)
#define rs (p<<1|1)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
#define lowbit(p) p&(-p)
using namespace std;typedef long long ll;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1e5+7;
const int M = 2007;
int n,k;
int ver[N << 1],edge[N << 1],nex[N << 1],head[N],tot;
int fa[N];
bool vis[N];
int dis[N];void add(int u,int v,int val){ver[tot] = v;edge[tot] = val;nex[tot] = head[u];head[u] = tot ++ ;
}void dfs(int x,int &t){//不能处理负值vis[x] = 1;for(int i = head[x];~i;i = nex[i]){int y = ver[i],z = edge[i];if(vis[y])continue;dis[y] = dis[x] + z;if(dis[y] >= dis[t])t = y;fa[y] = i;//存路径,用于成对变换,存的是前向星的指针dfs(y,t);}vis[x] = 0;//回溯
}void DP(int x,int &t){//可以处理负值vis[x] = 1;for(int i= head[x];~i;i = nex[i]){int y = ver[i],z = edge[i];if(vis[y])continue;DP(y,t);t = max(t,dis[x] + dis[y] + z);dis[x] = max(dis[x],dis[y] + z);}
}int main()
{scanf("%d%d",&n,&k);memset(head,-1,sizeof head);over(i,1,n-1){int x,y;scanf("%d%d",&x,&y);add(x,y,1);add(y,x,1);}int p = 1;//两次dfs找最远的p和qdfs(1,p);dis[p] = 0;//找到之后把它当成根节点fa[p] = -1;int q = p;dfs(p,q);int ans = ((n - 1) * 2) - dis[q] + 1;// + 1是因为新增了一条必须走一次的边if(k == 2){while(~fa[q]){//成对变换edge[fa[q]] = edge[fa[q]^1] = -1;q = ver[fa[q]^1];}q = 0;memset(dis,0,sizeof dis);DP(p,q);ans -= q - 1;}printf("%d\n",ans);return 0;
}

【树的直径】解题报告: luogu P3629 [APIO2010]巡逻(树的直径,位运算成对变换,思维)相关推荐

  1. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解

    题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...

  2. 洛谷 P1377 [TJOI2011]树的序 解题报告

    P1377 [TJOI2011]树的序 题目描述 众所周知,二叉查找树的形态和键值的插入顺序密切相关.准确的讲:1.空树中加入一个键值\(k\),则变为只有一个结点的二叉查找树,此结点的键值即为\(k ...

  3. 解题报告(二)C、(darkBZOJ 2194) 快速傅立叶之二(FFT、卷积的概念、常用变换)

    繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量题解和代码,题目难度不一定按照题号排序,我会在每道题后面加上题目难度指数(1∼51 \sim 51∼5),以模板题难度 11 ...

  4. 解题报告(二)C、(darkBZOJ 3771)Triple(生成函数 + FFT + 容斥原理)(3)

    繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量题解和代码,题目难度不一定按照题号排序,我会在每道题后面加上题目难度指数(1∼51 \sim 51∼5),以模板题难度 11 ...

  5. 解题报告(二)B、(P3338 [ZJOI2014])力(FFT经典套路)(2)

    繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量题解和代码,题目难度不一定按照题号排序,我会在每道题后面加上题目难度指数(1∼51 \sim 51∼5),以模板题难度 11 ...

  6. [解题报告]Codeforces 105D Entertaining Geodetics

    Abstract Codeforces 105D 并查集(官方标的) Body Source http://codeforces.com/problemset/problem/105/D Descri ...

  7. 青蛙的约会解题报告(转)

    那么什么是线性同余方程?对于方程:ax≡b(mod   m),a,b,m都是整数,求解x 的值. 解题例程:pku1061 青蛙的约会 解题报告 符号说明: mod表示:取模运算 ax≡b(mod   ...

  8. 树的直径【p3629】[APIO2010]巡逻

    Description 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一 ...

  9. uscao 线段树成段更新操作及Lazy思想(POJ3468解题报告)

    线段树成段更新操作及Lazy思想(POJ3468解题报告) 标签: treequerybuildn2cstruct 2011-11-03 20:37 5756人阅读 评论(0) 收藏 举报  分类: ...

最新文章

  1. java对象头_浅谈java对象结构 对象头 Markword
  2. 【RecyclerView】 七、RecyclerView.ItemDecoration 条目装饰 ( getItemOffsets 边距设置 )
  3. 查询进程并杀死该进程
  4. Moved to Google Blogger: hongstudios.blogspot.com
  5. C# == 和equals()区别
  6. sqlplus连接不上oracle,环境变量设置错误导致sqlplus 连接不上oracle
  7. springboot集成springDataJpa
  8. Oracle 制造死锁和查询死锁
  9. 2019/01/29-Linux目录
  10. 3DSmax里的nurms toggle命令中文版是什么意思
  11. 【fake_useragent】网络爬虫获取随机User-Agent
  12. CDBN卷积深度信念网
  13. 西门子SMART 200 modbus rtu通讯宇电温控器例程
  14. 智掌柜扫码点单,帮助店家解决开店烦恼
  15. 锂电池充电过程的四个阶段
  16. 使用Origin将不同的图片结合在一起
  17. BNUOJ 53073 【找规律】
  18. 向日葵远程软件快速重新联网,不再每次都等待100s!
  19. 浙江大学计算机学院 耿卫东教授 是哪个实验室的,浙江大学CG
  20. 文字转语音小助手有哪些?分享三款,配音免费还易操作

热门文章

  1. 详解 | 自动泊车中鱼眼相机实现车位线感知
  2. 基于机器视觉的智能人机交互技术
  3. 12个现实世界中的机器学习真相
  4. java自动装箱性能
  5. wget使用代理下载
  6. Dan Gillmor总结微软付费平息纠纷历史
  7. 学了js php就简单,学习笔记:JS + 简单的PHP实现用户注册及登录
  8. Python实现:详解LRU缓存淘汰算法
  9. 想转行?零基础该如何学Python?这些一定要明白
  10. 大触教你如何调节python内置函数