题目大意

给定一棵基环树, 问你有多少条路径的长度\(\ge K\).

点数\(\le 10^5\)

Solution

基环树分治模板题.

我是这样做的: 加边的时候用并查集维护点的连通性, 少加入环上的一条边, 使得基环图变为树的形态.

首先在树上进行一次常规的树分治. 我们能得到不经过被删除的一条边的满足要求的路径数量;

然后我们根据被删去的边的两个端点找到环, 在环上求出经过被删去边的路径数量即可.

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm> using namespace std;
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1; char c;
while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
}
const int N = (int)4e5;
int n, m, len;
int lp[2];
long long ans;
struct disjointSet
{
int pre[N + 1];
inline void initialize() {for(int i = 1; i <= n; ++ i) pre[i] = i;}
inline int access(int u)
{
if(pre[u] != u) pre[u] = access(pre[u]);
return pre[u];
}
}st;
struct segmentTree
{
struct node
{
node *suc[2];
int sz;
inline node() {for(int i = 0; i < 2; ++ i) suc[i] = NULL; sz = 0;}
}*rt;
void clear(node *u)
{
for(int i = 0; i < 2; ++ i) if(u->suc[i] != NULL) clear(u->suc[i]);
delete u;
}
inline void clear()
{
if(rt != NULL) clear(rt); rt = NULL; // 记得要把rt复位, 否则就会乱套啦
}
node* modify(node *u, int L, int R, int pos, int dlt)
{
if(u == NULL) u = new node;
u->sz += dlt;
if(L == R) return u;
if(pos <= L + R >> 1) u->suc[0] = modify(u->suc[0], L, L + R >> 1, pos, dlt);
else u->suc[1] = modify(u->suc[1], (L + R >> 1) + 1, R, pos, dlt);
return u;
}
inline void modify(int pos, int dlt)
{
rt = modify(rt, 1, n, pos, dlt);
}
int query(node *u, int L, int R, int pos)
{
if(u == NULL) return 0;
if(L >= pos) return u->sz;
if(pos > L + R >> 1) return query(u->suc[1], (L + R >> 1) + 1, R, pos);
else return query(u->suc[0], L, L + R >> 1, pos) + query(u->suc[1], (L + R >> 1) + 1, R, pos);
}
inline int query(int pos)
{
return query(rt, 1, n, pos);
}
}seg;
struct tree
{
struct node
{
vector<int> edg;
int flg, mx, sz;
int nxt, lst, len;
inline node() {edg.clear(); flg = 0;}
}nd[N + 1];
inline void addEdge(int u, int v)
{
int rtU = st.access(u), rtV = st.access(v);
if(rtU == rtV)
{
lp[0] = u, lp[1] = v;
return;
}
st.pre[rtU] = rtV;
nd[u].edg.push_back(v); nd[v].edg.push_back(u);
}
void getSize(int u, int pre)
{
// printf("%d\n", u);
nd[u].sz = 1; nd[u].mx = 0;
for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg) getSize(v, u), nd[u].sz += nd[v].sz, nd[u].mx = max(nd[v].sz, nd[u].mx);
// for(vector<int>::iterator p = nd[u].edg.begin(); p != nd[u].edg.end(); ++ p)
// if(*p != pre && ! nd[*p].flg) getSize(*p, u), nd[u].sz += nd[*p].sz, nd[u].mx = max(nd[*p].sz, nd[u].mx);
}
int getRoot(int u, int pre, int cen)
{
nd[u].mx = max(nd[u].mx, nd[cen].sz - nd[u].sz);
int res = u;
for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg)
{
int cur = getRoot(v, u, cen);
if(nd[cur].mx < nd[res].mx) res = cur;
}
return res;
}
void getAnswer(int u, int pre, int cur)
{
ans += seg.query(len - cur);
for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg) getAnswer(v, u, cur + 1);
}
void update(int u, int pre, int cur)
{
seg.modify(cur, 1);
for(auto v : nd[u].edg) if(v != pre && ! nd[v].flg) update(v, u, cur + 1);
}
inline void work(int u)
{
getSize(u, -1);
u = getRoot(u, -1, u); nd[u].flg = 1;
seg.clear(); seg.modify(1, 1);
for(auto v : nd[u].edg) if(! nd[v].flg)
{
getAnswer(v, u, 1);
update(v, u, 2);
}
for(auto v : nd[u].edg) if(! nd[v].flg) work(v);
}
inline void work() {work(1);}
int getLoop(int u, int pre)
{
nd[u].lst = pre; nd[u].nxt = -1;
if(u == lp[1]) return u;
for(auto v : nd[u].edg) if(v != pre && nd[u].nxt == -1) nd[u].nxt = getLoop(v, u);
if(~ nd[u].nxt) return u;
nd[u].lst = -1;
return -1;
}
void getOutsideSize(int u, int pre, int cur, int opt)
{
seg.modify(cur, opt);
for(auto v : nd[u].edg) if(v != pre) getOutsideSize(v, u, cur + 1, opt);
}
void getOutsideSize(int u, int cur)
{
seg.modify(cur, 1);
nd[u].len = cur;
for(auto v : nd[u].edg) if(v != nd[u].nxt && v != nd[u].lst) getOutsideSize(v, u, cur + 1, 1);
if(~ nd[u].lst) getOutsideSize(nd[u].lst, cur + 1);
}
void getLoopAnswer(int u, int pre, int cur)
{
ans += seg.query(len - cur);
for(auto v : nd[u].edg) if(v != pre) getLoopAnswer(v, u, cur + 1);
}
void getLoopAnswer(int u, int cur)
{
seg.modify(nd[u].len, -1);
for(auto v : nd[u].edg) if(v != nd[u].nxt && v != nd[u].lst) getOutsideSize(v, u, nd[u].len + 1, -1);
ans += seg.query(len - cur);
for(auto v : nd[u].edg) if(v != nd[u].nxt && v != nd[u].lst) getLoopAnswer(v, u, cur + 1);
if(~ nd[u].nxt) getLoopAnswer(nd[u].nxt, cur + 1);
}
inline void workOnLoop()
{
getLoop(lp[0], -1);
seg.clear();
getOutsideSize(lp[1], 1);
getLoopAnswer(lp[0], 1);
}
}T;
int main()
{ #ifndef ONLINE_JUDGE freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout); #endif using namespace Zeonfai;
n = getInt(), m = getInt(), len = getInt();
st.initialize();
for(int i = 0, u, v; i < m; ++ i) u = getInt(), v = getInt(), T.addEdge(u, v);
T.work();
if(n == m) T.workOnLoop();
printf("%lld\n", ans);
}

XSY 1749 tree的更多相关文章

  1. [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法

    二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...

  2. SAP CRM 树视图(TREE VIEW)

    树视图可以用于表示数据的层次. 例如:SAP CRM中的组织结构数据可以表示为树视图. 在SAP CRM Web UI的术语当中,没有像表视图(table view)或者表单视图(form view) ...

  3. 无限分级和tree结构数据增删改【提供Demo下载】

    无限分级 很多时候我们不确定等级关系的层级,这个时候就需要用到无限分级了. 说到无限分级,又要扯到递归调用了.(据说频繁递归是很耗性能的),在此我们需要先设计好表机构,用来存储无限分级的数据.当然,以 ...

  4. 2000条你应知的WPF小姿势 基础篇<45-50 Visual Tree&Logic Tree 附带两个小工具>

    在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000Things You Should Know About C# 和 2,0 ...

  5. Leetcode 笔记 110 - Balanced Binary Tree

    题目链接:Balanced Binary Tree | LeetCode OJ Given a binary tree, determine if it is height-balanced. For ...

  6. Leetcode 笔记 100 - Same Tree

    题目链接:Same Tree | LeetCode OJ Given two binary trees, write a function to check if they are equal or ...

  7. Leetcode 笔记 99 - Recover Binary Search Tree

    题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...

  8. Leetcode 笔记 98 - Validate Binary Search Tree

    题目链接:Validate Binary Search Tree | LeetCode OJ Given a binary tree, determine if it is a valid binar ...

  9. Leetcode 笔记 101 - Symmetric Tree

    题目链接:Symmetric Tree | LeetCode OJ Given a binary tree, check whether it is a mirror of itself (ie, s ...

随机推荐

  1. OpenCV学习笔记(八) 边缘、线与圆的检测

    边缘检测 对图像进行边缘检测之前,一般都需要先进行降噪(可调用GaussianBlur函数). Sobel算子 与 Scharr算子 都是一个离散微分算子 (discrete differentiat ...

  2. springboot学习资料汇总

    收集Spring Boot相关的学习资料,Spring Cloud点这里 推荐博客 纯洁的微笑 程序猿DD liaokailin的专栏 Spring Boot 揭秘与实战 系列 catoop的专栏 简 ...

  3. 洛谷P1162 填涂颜色

    题目链接:https://www.luogu.org/problemnew/show/P1162 这道题是LITTLESUN写的第一道BFS哦! 对于这道题的的思路是把封闭图形外边的0标记一边,在最后 ...

  4. 使用Jmeter做性能测试

    上周刚刚做完项目的性能测试.今天整理和总结一下,随便分享给大家. 首页呢,测试前,我们是有明确的性能指标的,而且测试环境和数据都已准备好,业务分析.场景分析大家根据自己的项目系统进行分析设计,我们选用 ...

  5. 精通CSS高级Web标准解决方案(6、对表单与表格应用样式)

    使用fieldset input[type="text"] { width:200px; } input:focus,textarea:focus{background:#ffc; ...

  6. c++面试须知

    这些都是从zhihu上看到的. 指针,多态(虚函数表.内存layout),作用域,内存的管理 算法与数据结构,数据结构上由掌握哈希.优先级队列,算法上有字符串处理,简单的DFS.BFS.动态规划 系统 ...

  7. 位图 c++ 位图排序

    什么是位图?来自http://www.cnblogs.com/dolphin0520/archive/2011/10/19/2217369.html 位图就是用一个bit来标记某个元素对应的值,键值就 ...

  8. cookie和session机制区别

    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端 ...

  9. 打开任意位置的webConfig

    请阅读原文:打开任意的配置文件 翻翻ConfigurationManager的签名,有一个方法吸引了我的注意:OpenExeConfiguration(string exePath).看上去我可以把B ...

  10. UVALive 5099 Nubulsa Expo 全局最小割问题

    B - Nubulsa Expo Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit S ...