传送门

题意:

给出一颗含有\(n\)个结点的无根树,之后给出一个长度为\(m\)的序列,每个元素在\([1,n]\)之间。

现在序列中每个长度为偶数的区间的完成时间定义为树上最小配对方法中每对匹配点间距离的总和。

现在要求所有长度为偶数的区间的完成时间的和。

思路:

  • 首先不妨将这颗树转化为有根树,最终不会影响答案。
  • 注意到性质:偶数个点的两两匹配方式是唯一的,都是最深的两个点相互匹配,这样才能保证没有重复计算的边。
  • 在子树内部直接计算不好算,要考虑很多东西(一开始就想偏了QAQ)。因为匹配方式唯一,所以子树中若全部匹配完成,那么至多只会剩下一个点。显然,若剩下一个点,此时子树及其父亲这条边肯定会算上。
  • 我们将问题转化为单独考虑一条边的贡献,一条边的贡献次数即为序列元素在该子树中出现奇数次的偶数区间个数。
  • 考虑暴力计算:对于当前的子树,暴力给序列上打上标记,然后做一个前缀和,若存在\(i,j,i<j\),满足\(i\equiv j(mod\ 2)\)且\(s_j\equiv s_i+1(mod\ 2)\)即可。
  • 如果用线段树维护,那么只需要维护奇数位置、偶数位置上面前缀模\(2\)意义下\(1\)的个数即可快速求得当前子树内部的答案。
  • 时间复杂度\(O(n^2logn)\)。
  • 因为这涉及到子树问题,且不带修改,所以可以直接施展\(dsu\ on\ tree\),最终复杂度为\(O(nlog^2n)\)。

感觉这种将子树内部的问题转化为子树+一条边的问题似乎是一种套路?(上次atcoder有个题也是这样)。

以后这种计算贡献的题可以尝试转换一下思路,单独考虑每个元素的贡献。

这个题还可以直接线段树合并来写,复杂度是\(O(nlogn)\),不过(不会写)懒得写了。。

代码如下:

/*
* Author: heyuhhh
* Created Time: 2019/11/16 8:54:36
*/
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
//#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 998244353; template <class T>
inline void read(T& x) {
static char c;
x = 0;
bool sign = 0;
while (!isdigit(c = getchar()))
if (c == '-')
sign = 1;
for (; isdigit(c); x = x * 10 + c - '0', c = getchar())
;
if (sign)
x = -x;
} int n, m;
int a[N];
struct Edge{
int v, w, next;
}e[N << 1];
int head[N], tot;
void adde(int u, int v, int w) {
e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
} int pre[N], last[N];
int sz[N], bson[N], son; void dfs(int u, int fa) {
int mx = 0; sz[u] = 1;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v != fa) {
a[v] = e[i].w;
dfs(v, u);
sz[u] += sz[v];
if(sz[v] > mx) mx = sz[v], bson[u] = v;
}
}
} int odd[N << 2], even[N << 2], rev[N << 2];
int ans; void Reverse(int o, int l, int r) {
if(l == r) {
if(l & 1) odd[o] ^= 1;
else even[o] ^= 1;
} else {
odd[o] = (r + 1) / 2 - l / 2 - odd[o];
even[o] = r / 2 - (l - 1) / 2 - even[o];
}
rev[o] ^= 1;
} void push_up(int o) {
odd[o] = odd[o << 1] + odd[o << 1|1];
even[o] = even[o << 1] + even[o << 1|1];
} void push_down(int o, int l, int r) {
if(rev[o]) {
int mid = (l + r) >> 1;
Reverse(o << 1, l, mid);
Reverse(o << 1|1, mid + 1, r);
rev[o] = 0;
}
} void upd(int o, int l, int r, int L, int R) {
if(L <= l && r <= R) {
Reverse(o, l, r);
return;
}
push_down(o, l, r);
int mid = (l + r) >> 1;
if(L <= mid) upd(o << 1, l, mid, L, R);
if(R > mid) upd(o << 1|1, mid + 1, r, L, R);
push_up(o);
} int Get() {
int res = (1ll * (m / 2 + 1 - even[1]) * even[1] % MOD + 1ll * ((m + 1) / 2 - odd[1]) * odd[1] % MOD) % MOD;
return res;
} void calc(int u, int fa) {
for(int i = last[u]; i; i = pre[i]) upd(1, 1, m, i, m);
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v == fa || v == son) continue;
calc(v, u);
}
} void dfs2(int u, int fa, int op) {
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v != fa && v != bson[u]) dfs2(v, u, 0);
}
if(bson[u]) dfs2(bson[u], u, 1);
son = bson[u];
calc(u, fa);
son = 0;
dbg(u, get());
ans = (ans + 1ll * a[u] * Get() % MOD) % MOD;
if(!op) calc(u, fa);
} void run(){
memset(head, -1, sizeof(head));
read(n), read(m);
for(int i = 1; i < n; i++) {
int u, v, w;
read(u), read(v), read(w);
adde(u, v, w); adde(v, u, w);
}
for(int i = 1; i <= m; i++) {
int x; read(x);
pre[i] = last[x];
last[x] = i;
}
dfs(1, 0);
dfs2(1, 0, 1);
cout << ans << '\n';
} int main() {
run();
return 0;
}

【UOJ388】配对树(dsu on tree+线段树)的更多相关文章

  1. BZOJ.3307.雨天的尾巴(dsu on tree/线段树合并)

    BZOJ 洛谷 \(dsu\ on\ tree\).(线段树合并的做法也挺显然不写了) 如果没写过\(dsu\)可以看这里. 对修改操作做一下差分放到对应点上,就成了求每个点子树内出现次数最多的颜色, ...

  2. 【cf375】D. Tree and Queries(dsu on tree+线段树)

    传送门 题意: 给出一颗以\(1\)为根的有根树,每个结点有个颜色\(c_i\). 之后要回答\(m\)组询问,每组询问包含\(v_i,k_i\),要回答以\(v_i\)为根的子树中,颜色出现次数不小 ...

  3. poj 3237 Tree(树链剖分,线段树)

    Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7268   Accepted: 1969 Description ...

  4. HDU 3333 Turing Tree 线段树+离线处理

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3333 Turing Tree Time Limit: 6000/3000 MS (Java/Othe ...

  5. 树(一)——线段树

    问题 现在有1~30这30个数,数N被抽上的概率正比于1/sqrt(N+1),求满足这个概率分布的随机数发生器. 思路 第一,如何解决这个"概率正比"问题. 第二,如何产生满足条件 ...

  6. bzoj 4034 [HAOI2015] T2(树链剖分,线段树)

    4034: [HAOI2015]T2 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1536  Solved: 508[Submit][Status] ...

  7. bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit ...

  8. bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status ...

  9. HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)

    Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Tota ...

随机推荐

  1. [Linux] 低版本centos升级git解决fatal: HTTP request failed

    编译用的一些依赖yum install curl-devel expat-devel gettext-devel openssl-devel zlib-develyum install gcc per ...

  2. RPM命令总结

    RPM软件管理程序 rpm命令是RPM软件包的管理工具.rpm原本是Red Hat Linux发行版专门用来管理Linux各项套件的程序,由于它遵循GPL规则且功能强大方便,因而广受欢迎.逐渐受到其他 ...

  3. Python入门基础学习(文件与异常处理)

    Python基础学习笔记(七) 捕获异常的语法格式: 文件的基本操作: 打开文件 读.写文件 关闭文件 read方法 --读取文件: open函数的第一个参数是要打开的文件名(文件名区分大小写) 如果 ...

  4. java8-08-创建stream流

    为什么用stream                             应用函数式编程  配合Lamdba表达式 更快操作集合类 数组      什么是 stream               ...

  5. 算法问题实战策略 MEETINGROOM 附一份tarjan模板

    地址 https://algospot.com/judge/problem/read/MEETINGROOM 解答  2-sat 代码样例过了 没有ac. 我又没有正确代码对拍..... 已确认是输出 ...

  6. Python爬虫教程-使用chardet

    Spider-03-使用chardet继续学习python爬虫,我们经常出现解码问题,因为所有的页面编码都不统一,我们使用chardet检测页面的编码,尽可能的减少编码问题的出现 网页编码问题解决使用 ...

  7. Python模块import本质是什么?import是什么

    ​ 写这篇文章主要是对Python的import模块或包的机制有了更深层级的一个理解,也在具体工作中得到了一点实践,这种思考是由上一篇文章<__main__内置模块预加载Shotgun接口的妙用 ...

  8. 《细说PHP》第四版 样章 第23章 自定义PHP接口规范 2

    23.1.3  接口的应用和优势 API是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无须访问源码,或理解内部工作机制的细节.接口应用的一些常见场景如下 ...

  9. Jmeter-Java请求实战

    1.1. jmeter-java插件实现接口测试 (linux /mysql/rabbit-mq) 本次需要准备环境 Eclipse+jdk8 Jmeter Python 1.1.1. Rabbit- ...

  10. Linux - 几种方法来实现scp拷贝时无需输入密码

    前言 在实际工作中,经常会将本地的一些文件传送到远程的机器上.scp是一个很好用的命令,缺点是需要手工输入密码. 如何在shell脚本中实现传输文件,而不用手工输入密码呢?接下来介绍三种方法. 一.建 ...