题目大意

给定一棵基环树, 问你有多少条路径的长度\(\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. Go语言之反射(二)

    反射的值对象 反射不仅可以获取值的类型信息,还可以动态地获取或者设置变量的值.Go语言中使用reflect.Value获取和设置变量的值. 使用反射值对象包装任意值 Go语言中,使用reflect.V ...

  2. Couchbase I

    Couchbase第一印象(架构特性) 面向文档 保存的字节流总有一个 DOCUMENT ID(Object_ID) 高并发性,高灵活性,高拓展性,容错性好 面向文档的集群存储系统 每个文档用一个唯一 ...

  3. leetcode 【 Remove Duplicates from Sorted Array II 】python 实现

    题目: Follow up for "Remove Duplicates":What if duplicates are allowed at most twice? For ex ...

  4. C++ STL map容器的说明测试1

    // maptest.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h" /*********************************** ...

  5. seajs引入jquery

    seajs 2.2.1在config文件中preload一次jquery,就可以在整个项目中使用jquery.如下: seajs.on('exec', function(module) { if (m ...

  6. linux常用的日志分析脚本

    linux实用的日志分析脚本 日志分析 随意的tail一个access_log文件,下面是一条经典的访问记录 /Dec/::: +] “GET /query/trendxml/district/tod ...

  7. PHP 获取上一个页面的url

    php $_SERVER["HTTP_REFERER"]变量可以获取上一个或前一个页面的URL地址. 比如有一个a.php页面,这个页面上有一个链接指向b.php页面,如果我们在a ...

  8. 翻译MDN里js的一些方法属性

    TypeError The TypeError object represents an error when a value is not of the expected type. [TypeEr ...

  9. eclipse删除svn下载的文件后如何恢复

    Team右键-->与资源库同步-->覆盖/更新

  10. Puppet单机实战之Nginx代理Tomcat

    author:JevonWei 版权声明:原创作品 blog:http://119.23.52.191/ --- 构建实战之Nginx代理Tomcat [root@node1 modules]# mk ...