Solution

正解是一个\(\log\)的link-cut tree. 将一条边拆成两个事件, 按照事件排序, link-cut tree维护联通块大小即可.

link-cut tree维护子树大小非常不熟练. 正确的做法是每个点开两个变量sizeadd, 分别表示在splay中以这个点为根的所有点所在的子树的点的数量, 以及以当前点为根的子树由虚边贡献的点的数量.

#include <cstdio>
#include <cctype>
#include <algorithm>
#define sort std::sort
#define swap std::swap 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)2e5;
struct edge
{
int u, v, pos, opt;
inline edge() {}
inline edge(int _u, int _v, int _pos, int _opt) {u = _u; v = _v; pos = _pos; opt = _opt;}
inline int friend operator <(edge a, edge b)
{
return a.pos == b.pos ? a.opt < b.opt : a.pos < b.pos;
}
}edg[N << 1];
struct linkCutTree
{
struct node
{
node *pre, *suc[2];
int isRoot, rev, sz, ad; // ad表示虚边连过来的大小, sz表示整颗辅助树的大小
inline node() {pre = NULL; for(int i = 0; i < 2; ++ i) suc[i] = NULL; isRoot = sz = 1; rev = ad = 0;}
inline int getRelation() {return isRoot ? -1 : this == pre->suc[1];}
inline void update() {sz = ad + 1; for(int i = 0; i < 2; ++ i) if(suc[i] != NULL) sz += suc[i]->sz;}
inline void reverse()
{
if(! isRoot) pre->reverse();
if(rev)
{
swap(suc[0], suc[1]); rev = 0;
for(int i = 0; i < 2; ++ i) if(suc[i] != NULL) suc[i]->rev ^= 1;
}
}
}nd[N + 1];
inline void rotate(node *u)
{
node *pre = u->pre, *prepre = u->pre->pre; int k = u->getRelation();
if(u->suc[k ^ 1] != NULL) u->suc[k ^ 1]->pre = pre; pre->suc[k] = u->suc[k ^ 1];
u->pre = prepre; if(! pre->isRoot) prepre->suc[pre->getRelation()] = u;
u->suc[k ^ 1] = pre; pre->pre = u;
if(pre->isRoot) u->isRoot = 1, pre->isRoot = 0;
pre->update(); u->update();
}
inline void splay(node *u)
{
u->reverse();
while(! u->isRoot)
{
if(! u->pre->isRoot) rotate(u->getRelation() == u->pre->getRelation() ? u->pre : u);
rotate(u);
}
}
inline void access(node *u)
{
splay(u);
if(u->suc[1] != NULL) u->suc[1]->isRoot = 1, u->ad += u->suc[1]->sz, u->suc[1] = NULL;
while(u->pre != NULL)
{
splay(u->pre);
if(u->pre->suc[1] != NULL) u->pre->suc[1]->isRoot = 1, u->pre->ad += u->pre->suc[1]->sz;
u->pre->suc[1] = u; u->pre->ad -= u->sz; u->isRoot = 0;
splay(u);
}
}
inline void makeRoot(node *u)
{
access(u); u->rev ^= 1;
}
inline long long link(int _u, int _v)
{
node *u = nd + _u, *v = nd + _v;
makeRoot(u); makeRoot(v);
long long res = (long long)u->sz * v->sz;
v->pre = u; u->sz += v->sz; u->ad += v->sz;
return res;
}
inline void cut(int _u, int _v)
{
node *u = nd + _u, *v = nd + _v;
makeRoot(u); access(v);
v->suc[0] = NULL; u->isRoot = 1; u->pre = NULL; v->sz -= u->sz;
}
}LCT;
int main()
{ #ifndef ONLINE_JUDGE freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout); #endif using namespace Zeonfai;
int n = getInt();
for(int i = 0, u, v, L, R; i < n - 1; ++ i) u = getInt(), v = getInt(), L = getInt(), R = getInt(), edg[i << 1] = edge(u, v, L, 1), edg[i << 1 | 1] = edge(u, v, R + 1, 0);
sort(edg, edg + (n - 1 << 1));
long long ans = 0;
for(int i = 0; i < n - 1 << 1; ++ i) if(! edg[i].opt) LCT.cut(edg[i].u, edg[i].v); else ans += LCT.link(edg[i].u, edg[i].v);
printf("%lld\n", ans);
}

2016北京集训测试赛(十四)Problem B: 股神小D的更多相关文章

  1. 2016北京集训测试赛(十四)Problem A: 股神小L

    Solution 考虑怎么卖最赚钱: 肯定是只卖不买啊(笑) 虽然说上面的想法很扯淡, 但它确实能给我们提供一种思路, 我们能不买就不买; 要买的时候就买最便宜的. 我们用一个优先队列来维护股票的价格 ...

  2. 2016北京集训测试赛(十六)Problem C: ball

    Solution 这是一道好题. 考虑球体的体积是怎么计算的: 我们令\(f_k(r)\)表示\(x\)维单位球的体积, 则 \[ f_k(1) = \int_{-1}^1 f_{k - 1}(\sq ...

  3. 2016北京集训测试赛(十六)Problem B: river

    Solution 这题实际上并不是构造题, 而是一道网络流. 我们考虑题目要求的一条路径应该是什么样子的: 它是一个环, 并且满足每个点有且仅有一条出边, 一条入边, 同时这两条边的权值还必须不一样. ...

  4. 2016北京集训测试赛(十六)Problem A: 任务安排

    Solution 这道题告诉我们, 不能看着数据范围来推测正解的时间复杂度. 事实证明, 只要常数足够小, \(5 \times 10^6\)也是可以跑\(O(n \log n)\)算法的!!! 这道 ...

  5. 2016北京集训测试赛(十)Problem A: azelso

    Solution 我们把遇到一个旗子或者是遇到一个敌人称为一个事件. 这一题思路的巧妙之处在于我们要用\(f[i]\)表示从\(i\)这个事件一直走到终点这段路程中, \(i\)到\(i + 1\)这 ...

  6. 2016北京集训测试赛(八)Problem C: 直径

    Solution 一个定理: 把两棵树用一条边练成一棵树后, 树的直径在原来两棵树的四个直径端点中产生. 放到这一题, 我们通过DP先求出大树中以每个点为根的子树中的直径, 再取每棵小树中与其他树有连 ...

  7. 【2016北京集训测试赛(十)】 Azelso (期望DP)

    Time Limit: 1000 ms   Memory Limit: 256 MB Description 题解 状态表示: 这题的状态表示有点难想...... 设$f_i$表示第$i$个事件经过之 ...

  8. 【2016北京集训测试赛(十六)】 River (最大流)

    Description  Special Judge Hint 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. 题解 题目大意:给定两组点,每组有$n$个点,有若干条跨组 ...

  9. 2016北京集训测试赛(十七)Problem C: 数组

    Solution 线段树好题. 我们考虑用last[i]表示\(i\)这个位置的颜色的上一个出现位置. 考虑以一个位置\(R\)为右端点的区间最远能向左延伸到什么位置: \(L = \max_{i \ ...

随机推荐

  1. go经典练习题涉及流程控制-字符串-struct-map的数据类型的处理

    one:求1到100之间的质数 package main import ( "fmt" ) func isPrime(n int) bool { var flag = true f ...

  2. 如何选择Android自动化框架的几点拙见

    首先由于我自己也是个新手,也是在学习各种框架然后给公司项目选定相应自动化框架,研究移动自动化测试框架也就近段时间而已,所以我只能从我自己今天为止的认知角度给各个框架抒发我自己的拙见,你看是否能从中接纳 ...

  3. 更改 Mac 上的功能键行为

    您可以将 Apple 键盘上的顶行按键用作标准功能键,或用来控制 Mac 的内建功能.   如果您的 Apple 键盘部分顶行按键上印有图标,则这些按键可用于执行每个图标所示的特殊功能.这些按键也可用 ...

  4. 软工实践 - 第十次作业 Alpha 冲刺 (2 / 10)

    队名:起床一起肝活队 组长博客:https://www.cnblogs.com/dawnduck/p/9960710.html 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去 ...

  5. POJ2796 Feel Good -- 单调队列

    Feel Good Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 14489   Accepted: 4015 Case T ...

  6. 【转】unity下的Line of Sight(LOS)的绘制

    http://www.cnblogs.com/yangrouchuan/p/6366629.html 先说说什么是Linf of Sight.在很多RTS游戏中,单位与单位之间的视野关系经常会受到障碍 ...

  7. activemq 简单聊天

    有兴趣加群qq:200634530

  8. python正则 转

    python中的正则表达式(re模块)   一.简介 正则表达式本身是一种小型的.高度专业化的编程语言,而在python中,通过内嵌集成re模块,程序媛们可以直接调用来实现正则匹配.正则表达式模式被编 ...

  9. POJ 3321:Apple Tree(dfs序+树状数组)

    题目大意:对树进行m次操作,有两类操作,一种是改变一个点的权值(将0变为1,1变为0),另一种为查询以x为根节点的子树点权值之和,开始时所有点权值为1. 分析: 对树进行dfs,将树变为序列,记录每个 ...

  10. python爬虫异常处理

    import urllib2 try: response = urllib2.urlopen('http://www.baidu.com') except urllib2.URLError, e: p ...