D4 树的直径、重心以及基环树
第一题第二题鉴上我前几篇博客poj1985 poj1849:https://www.cnblogs.com/Tyouchie/p/10384379.html
第三题:数的重心;poj1655
来自sjh大佬的版子,邻接表写法
#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=2e4+;
template<typename T>inline void read(T &x)
{
x=;
T f=,ch=getchar();
while (!isdigit(ch) && ch^'-') f=-, ch=getchar();
if (ch=='-') f=-, ch=getchar();
while (isdigit(ch)) x=(x<<)+(x<<)+(ch^), ch=getchar();
x*=f;
}
int vis[maxn],siz[maxn],ans,pos,n;
int ver[maxn<<],Next[maxn<<],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
inline void dfs(int x)
{
vis[x]=,siz[x]=;
int maxpart=;
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (!vis[y])
{
dfs(y);
siz[x]+=siz[y];
maxpart=max(maxpart,siz[y]);
}
}
maxpart=max(maxpart,n-siz[x]);
if (maxpart<ans || (maxpart==ans && x<pos))
ans=maxpart,pos=x;
}
int main()
{
int t;read(t);
while (t--)
{
read(n);
memset(vis,,sizeof(vis));
memset(head,,sizeof(head));
len=pos=;
ans=0x3f3f3f3f;
for (int x,y,i=;i<n;++i)
{
read(x);read(y);
add(x,y);add(y,x);
}
dfs();
printf("%d %d\n",pos,ans);
}
return ;
}
我写的邻接矩阵
#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=2e4+;
const int INF=0x3f3f3f3f;
vector<int>G[maxn];
int son[maxn];
int ans,n,balance,t; void dfs(int v,int fa)
{
son[v]=;
int d=G[v].size();
int pre_balance=;
for (int i=;i<d;i++)
{
int k=G[v][i];
if (k!=fa)
{
dfs(k,v);
son[v]+=son[k];
pre_balance=max(pre_balance,son[k]);
}
}
pre_balance=max(pre_balance,n-son[v]);
if (pre_balance<balance || (pre_balance==balance&&v<ans))
{
ans=v;
balance=pre_balance;
}
}
int main()
{
cin>>t;
while (t--)
{
scanf("%d",&n);
for (int i=;i<=n;++i)
G[i].clear();
for (int i=;i<n;i++)
{
int s,e;
scanf("%d%d",&s,&e);
G[s].push_back(e);
G[e].push_back(s);
}
memset(son,,sizeof(son));
ans=;balance=INF;
dfs(,);
cout<<ans<<' '<<balance<<endl;
}
return ;
}
Noip 2018 旅行;luogu5022
题意:一个n个点,m条边的连通图。可以从任意一个点出发,前往任意 一个相邻的未访问的结点,或沿着上一次来这个点的边返回。需要遍历 每一个点。每经过一个新的结点,就将这个结点写下来。最终可以得到 一个序列。求字典序最小的序列。 n ≤ 5000, m ≤ n。
貌似用栈慢一点,洛谷最后一个点过不了,要用02优化;
但是用栈维护单调性比较方便;
n < m:对于树的情况,显然从1出发,每次从字典序最小的相邻结 点DFS即可。
n = m: 对于有环的情况,由于环只有一个,我们可以将环找出来, 枚举删掉环上的每一条边,然后按树的情况求解即可。 时间复杂度O(n 2 )。
#include <algorithm>
#include <cctype>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstring>
#include <deque>
#include <functional>
#include <list>
#include <map>
#include <iomanip>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define R register
using namespace std;
typedef long long ull;
const int maxn = 5e3 + ; inline int read() {
int s = , w = ;
char ch = getchar();
while (!isdigit(ch)) { if (ch == '-') w = -; ch = getchar(); }
while (isdigit(ch)) { s = (s << ) + (s << ) + (ch ^ ); ch = getchar(); }
return s * w;
} inline void write(int x)
{
if(x < ) putchar('-'),x = -x;
if(x > ) write(x / );
putchar(x % + '');
} vector <int> vec[maxn];
int ans[maxn], edge[maxn][], t[maxn], vis[maxn];
int n, m, da, db, tsize = ; inline void dfs(int x) {
t[++tsize] = x; vis[x] = ;
int l = vec[x].size();
for (R int i = ; i < l; ++i) {
int y = vec[x][i];
if (!vis[y] && !((x == da && y == db) || (x == db && y == da))) dfs(y);
}
return ;
} inline void check() {
if (tsize != n) return ;
for (R int i = ; i <= n; ++i) {
if (t[i] != ans[i]) {
if (t[i] > ans[i]) return ;
break;
}
}
for (R int i = ; i <= n; ++i) {
ans[i] = t[i];
}
return ;
} int main() {
memset(ans, 0x3f, sizeof(ans));
n = read(), m = read();
for (R int i = ; i <= m; ++i) {
int a = read(), b = read();
vec[a].push_back(b);
vec[b].push_back(a);
edge[i][] = a;
edge[i][] = b;
}
for (R int i = ; i <= n; ++i) sort(vec[i].begin(), vec[i].end());
if (n > m) {
da = -, db = -;
dfs();
check();
}
else {
for (R int i = ; i <= m; ++i) {
tsize = ;
da = edge[i][];
db = edge[i][];
memset(vis, , sizeof(vis));
dfs();
check();
}
}
for (R int i = ; i <= n; ++i) write(ans[i]), putchar(' ');
return ;
}
第四题:BZOJ 1791
跑一个基环树直径;
来自石神的模板;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+;
template<typename T>inline void read(T &x)
{
x=;
T f=,ch=getchar();
while (!isdigit(ch)) ch=getchar();
if (ch=='-') f=-, ch=getchar();
while (isdigit(ch)) x=(x<<)+(x<<)+(ch^), ch=getchar();
x*=f;
}
int n,m,t;//t是标识符
int ver[maxn<<],edge[maxn<<],next[maxn<<],head[maxn],tot,du[maxn];
void add(int x,int y,int z)
{
ver[++tot]=y,edge[tot]=z,next[tot]=head[x],head[x]=tot,++du[y];
}
int c[maxn];//环上的点
int v[maxn];
int q[maxn<<];
void bfs(int s,int t)
{
int l,r;
q[l=r=]=s;//手写队列维护
c[s]=t;//标记连通块(看每个节点属于哪个基环树)
while (l<=r)
{
for (int i=head[q[l]]; i; i=next[i])
if (!c[ver[i]])
{
q[++r]=ver[i];
c[ver[i]]=t;
}
l++;
}
}
ll f[maxn];//每颗子树的直径
ll d[maxn];//每个节点的子树大小
void topsort()//找环操作顺便处理一种情况(直径不经过环)
{
int l=,r=,x,y;
for (int i=; i<=n; ++i)
if (du[i]==)//无向图度数为1
q[++r]=i;
while (l<=r)
{
for (int i=head[x=q[l]]; i; i=next[i])
if (du[y=ver[i]]>)//度大于1可更新答案
{
d[c[x]]=max(d[c[x]],f[x]+f[y]+edge[i]);//子树内最长链
f[y]=max(f[y],f[x]+edge[i]);//f[x]表示x子树中离x最远的点的距离
if ((--du[y])==)
q[++r]=y;
}
l++;
}
}
ll a[maxn<<];
ll b[maxn<<];
void dp(int t,int x)
{
int m=,i,l=,r,y=x;
do
{
a[++m]=f[y];
du[y]=;
for (i=head[y]; i; i=next[i])
if (du[ver[i]]>)//点在环上
{
y=ver[i];
b[m+]=b[m]+edge[i];//b[i]表示环上x到i的距离
break;
}
} while (i);//此时答案为 f[i]+f[j]+dis[i][j]的最大值,dis[i][j]表示环上i到j的最远距离
if (m==)//跑到环外,需要特判
{
for (i=head[y]; i; i=next[i])
if (ver[i]==x)
l=max(l,edge[i]);
d[t]=max(d[t],f[x]+f[y]+l);
return;
}
for (i=head[y]; i; i=next[i])//连接环的首尾
if (ver[i]==x)
{
b[m+]=b[m]+edge[i];
break;
}
for (i=; i<m; ++i)//由于是环,所以复制一份
{
a[m+i]=a[i];
b[m+i]=b[m+]+b[i];
}
q[l=r=]=;
for (i=; i<*m; ++i)
{
while (l<=r && i-q[l]>=m) ++l;
d[t]=max(d[t],a[i]+a[q[l]]+b[i]-b[q[l]]);
while (l<=r && a[q[r]]+b[i]-b[q[r]]<=a[i]) --r;//单调队列维护
q[++r]=i;
}
}
int main()
{
read(n);
for (int i=; i<=n; ++i)
{
int x,y;
read(x);read(y);
add(i,x,y),add(x,i,y);
}
for (int i=; i<=n; ++i)
if (!c[i])
bfs(i,++t);//统计有多少基环树
topsort();//拓扑找环 memset(v,,sizeof(v));//重新利用v数组,当作基环树是否算过
ll ans=0ll;
for (int i=; i<=n; ++i)
if (du[i]> && !v[c[i]])//每个基环树只跑一遍并且此时i是环上一点
{
v[c[i]]=;
dp(c[i],i);//求基环树的直径
ans+=d[c[i]];
}
printf("%lld\n",ans);
return ;
}
第五题:LUOGU CF 835F
第六题“:BZOJ 1040:
题意:n个点n条边的图,每个点都有点权,要求找到一个点集,点集中的 点相互之间不能有边相连,最大化点集的权值和。 1 ≤ n ≤ 106
如果联通的话,就是一个基环树了,否则为基环树森林。
这道题可 以简单的抽象为:基环树的最大独立集。
如果是一棵树该怎么做?
DP。
f [i][0] =∑max(f [son[i]][0], f [son[i][1])
f [i][1] =∑f [son[i]][0]
在每一棵基环树的环上枚举一条边,记它的两个端点为u和v,然后 删掉这条边做树形dp即可。
从该边的两个端点出发选择:
1 强制不选u,v任意,环的贡献为以u做DP的f [u][0]。
2 强制不选v,u任意,环的贡献为以v做DP的f [v][0]。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5e2;
template<typename T>inline void read(T &x)
{
x=;
T f=,ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-, ch=getchar();
while (isdigit(ch)) x=(x<<)+(x<<)+(ch^), ch=getchar();
x*=f;
}
int ver[maxn<<],Next[maxn<<],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int n,power[maxn],hate[maxn],vis[maxn],U,V;
ll f[maxn],g[maxn];
inline void dfs(int x,int fa)//dfs找环
{
vis[x]=;
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (y!=fa)
{
if (!vis[y]) dfs(y,x);
else
{
vis[y]=;
U=x,V=y;
return ;
}
}
}
}
inline void tree_dp(int x,int fa,int rt,int ban)//ban 不选的点
{
vis[x]=;
f[x]=power[x];
g[x]=;
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (x==rt && i==ban) continue;
if (y!=fa && y!=rt)
{
tree_dp(y,x,rt,ban);
f[x]+=g[y];
g[x]+=max(g[y],f[y]);
}
}
}
int main()
{
read(n);
for (int i=,k;i<=n;++i)
{
read(power[i]);read(hate[i]);
add(i,hate[i]),add(hate[i],i);
}
ll ans=;
for (int i=;i<=n;++i)
if (!vis[i])
{
dfs(i,-);
int banu,banv;
for (int i=head[U];i;i=Next[i])
if (ver[i]==V)
{
banu=i;
break;
}
for (int i=head[V];i;i=Next[i])
if (ver[i]==U)
{
banv=i;
break;
}
tree_dp(U,-,U,banu);//断环为链并将断开的两个点强制其中一个点为根且不选,做一次树形DP
ll uans=g[U];
tree_dp(V,-,V,banv);//对另一个点做同样操作
ll vans=g[V];
ans+=max(uans,vans);//取两次结果最大值加入ans
}
printf("%lld\n",ans);
return ;
}
D4 树的直径、重心以及基环树的更多相关文章
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- 『Island 基环树直径』
Island(IOI 2008) Description 你准备浏览一个公园,该公园由 N 个岛屿组成,当地管理部门从每个岛屿 i 出发向另外一个岛屿建了一座长度为 L_i 的桥,不过桥是可以双向行走 ...
- BZOJ 1791: [IOI2008]Island 岛屿 - 基环树
传送门 题解 题意 = 找出无向基环树森林的每颗基环树的直径. 我们首先需要找到每颗基环树的环, 但是因为是无向图,用tarjan找环, 加个手工栈, 我也是看了dalao的博客才知道tarjan找无 ...
- [IOI2008/BZOJ1791 岛屿](处理基环树的小技巧&基于bfs树形DP)
IOI2008/BZOJ1791 岛屿 题目大意是在一个基环树森林里求每一棵基环树的直径①的和. 其实就是树的直径的基环树升级版.我们先把环找出来,然后从环上的每一个节点x出发,并且不经过环上其他节点 ...
- Solution -「基环树」做题记录
写的大多只是思路,比较简单的细节和证明过程就不放了,有需者自取. 基环树简介 简单说一说基环树吧.由名字扩展可得这是一类以环为基础的树(当然显然它不是树. 通常的表现形式是一棵树再加一条非树边,把图画 ...
- codeforces GYM 100114 J. Computer Network tarjan 树的直径 缩点
J. Computer Network Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Des ...
- HDU4607 - Park Visit(树的直径)
题目大意 给定一颗树,要求走过其中连续的k个点,使得步数最少 题解 每条边要么经过两次,要么一次,因为我们的目标就是使得走一次的边尽量的多,这样就转换成求树的直径了,求树的直径我用的是两次dfs,先随 ...
- BZOJ 1040 骑士 基环树 树形DP
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1040 题目大意: Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫 ...
- [10.12模拟赛] 老大 (二分/树的直径/树形dp)
[10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...
随机推荐
- Linux下的搜索查找命令的详解(find)
4.find Linux下find命令在目录结构中搜索文件,并执行指定的操作.Linux下find命令提供了相当多的查找条件,功能很强大.由于find具有强大的功能,所以它的选项也很多,其中大部分选项 ...
- python __get__ & __set__
目的: 提供类似java中的getter/setter的东西. (getter/setter的目的: 将属性方法化,使得属性的引用变得简单---尤其将来属性的读取/赋值有比较复杂的逻辑) 官方简明文 ...
- 防止xss和sql注入:JS特殊字符过滤正则
function stripscript(s) { var pattern = new RegExp("[%--`~!@#$^&*()=|{}':;',\\[\\].<> ...
- 《Python编程》课程报告 python技术在数据分析中的应用之网络爬虫
摘要:... 2 1 引言 :... 2 1.1课题研究背景和研究现状... 2 1.1.1课题背景和目的... 3 1.1.2研究现状... 4 1.1.2.1语言... 4 1.1 ...
- mybatis mapper-locations作用
application上配置了@MapperScan(扫面mapper类的路径)和pom.xml中放行了mapper.xml后,配置mapper-locations没有意义 查找后得知,如果mappe ...
- Compile caffe on unbutu 16.0.4
1. apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libboost-all-dev libhd ...
- linux iptables 防火墙简介
iptables防火墙简介 Netfilter/Iptables(以下简称Iptables)是unix/linux自带的一款优秀且开放源代码的安全自由的基于包过滤的防火墙工具,它的功能十分强大,使用非 ...
- Fiddler修改图片显示
培训课讲修改请求值,记录一下操作步骤: 步骤如下: 1. 点击人人网图片另存为到桌面 2. 打开fiddler,找到图片发送的请求(单击图片的链接,点击右边面板的Inspectors.查看ImageV ...
- 【学习笔记】Tensorflow+Inception-v3训练自己的数据
导读 喵喵的,一个大坑.本文分为吐槽和干货两部分. 一.吐槽 大周末的,被导师扣下加班,嗨气,谁叫本狗子太弱鸡呢,看起来很简单的任务倒腾了两天还没完,不扣你扣谁? 自己刚接到微调Inception-v ...
- python基础(11)-常用模块
re(正则)模块 常用方法 findall() 以列表返回所有满足条件的结果 import re print(re.findall('\d','a1b2c2abc123'))#['1', '2', ' ...