[UOJ388]配对树


题解
贪心+线段树
首先如果我们知道了哪些点是关键点应该怎么搞
显然最小的匹配方案所有的边至多被经过一次
可以考虑每条边的贡献
因为我们要贡献尽量小
所以我们尽量让每条边经过的人尽量少
那么每条边被经过的条件就是一条边连接的两个节点的子树内关键点数量是奇数
然后我们可以直接考虑每条边会被多少个区间影响
一条边能被一个区间影响的条件就是这个区间的大小是偶数且这个区间内在这个边的深度较深的端点的子树内的点的数量有奇数个
也就是满足\(j-i=0(\mod 2) s_j - s_i=1(\mod 2)\)
这个东西可以用线段树维护
就记录下标为\(奇数/偶数\)且前缀和为奇数的数量
那么修改就是修改从这个点到\(m\)都翻转,并且可以直接计算了
然后可以用线段树合并来处理这个东西
但是我写了半天写不动><
就直接上dsu on tree来搞子树了==
代码
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define ls (now << 1)
# define rs (now << 1 | 1)
const int M = 100005 ;
const int N = 20 ;
const int mod = 998244353 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , m ;
int num , hea[M] ;
int cnt , ans , fdis[M] ;
int size[M] , bsn[M] ;
vector < int > vec[M] ;
struct E {
int nxt , to , dis ;
} edge[M * 2] ;
struct Node {
bool rev ;
int odd , even ;
} t[M * N] ;
inline void add_edge(int from , int to , int dis) {
edge[++num].nxt = hea[from] ;
edge[num].to = to ;
edge[num].dis = dis ;
hea[from] = num ;
}
inline void Flip(int now , int l , int r) {
t[now].even = r / 2 - (l - 1) / 2 - t[now].even ;
t[now].odd = (r + 1) / 2 - l / 2 - t[now].odd ;
t[now].rev ^= 1 ;
}
inline void pushup(int now) {
t[now].odd = t[ls].odd + t[rs].odd ;
t[now].even = t[ls].even + t[rs].even ;
}
inline void pushdown(int now , int l , int r) {
if(t[now].rev) {
t[now].rev = 0 ; int mid = (l + r) >> 1 ;
if(ls) Flip(ls , l , mid) ; if(rs) Flip(rs , mid + 1 , r) ;
}
}
void Change(int L , int R , int l , int r , int now) {
if(!now || l > R || r < L) return ;
if(l >= L && r <= R) { Flip(now , l , r) ; return ; }
pushdown(now , l , r) ;
int mid = (l + r) >> 1 ;
if(mid >= R) Change(L , R , l , mid , ls) ;
else if(mid < L) Change(L , R , mid + 1 , r , rs) ;
else { Change(L , mid , l , mid , ls) ; Change(mid + 1 , R , mid + 1 , r , rs) ; }
pushup(now) ;
}
inline void Calc(int u) {
int sum_e = m / 2 + 1 , sum_o = (m + 1) / 2 ;
ans = ((ans + 1LL * fdis[u] * t[1].even % mod * (sum_e - t[1].even) % mod) % mod + 1LL * fdis[u] * t[1].odd % mod * (sum_o - t[1].odd) % mod) % mod ;
}
void fdfs(int u , int father) {
size[u] = 1 ; int mx = -1 ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ; if(v == father) continue ;
fdis[v] = edge[i].dis ;
fdfs(v , u) ; size[u] += size[v] ;
if(size[v] > mx) mx = size[v] , bsn[u] = v ;
}
}
inline void Add(int u) {
for(int i = 0 , x , sz = vec[u].size() ; i < sz ; i ++) {
x = vec[u][i] ;
Change(x , m , 1 , m , 1) ;
}
}
void Update(int u , int father) {
Add(u) ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
if(v != father)
Update(v , u) ;
}
}
void dfs(int u , int father , bool Kp) {
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
if(v != father && v != bsn[u])
dfs(v , u , 0) ;
}
if(bsn[u]) dfs(bsn[u] , u , 1) ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ;
if(v != father && v != bsn[u])
Update(v , u) ;
}
Add(u) ;
if(fdis[u]) {
int sum_e = m / 2 + 1 , sum_o = (m + 1) / 2 ;
ans = (ans + 1LL * fdis[u] * t[1].even % mod * (sum_e - t[1].even) % mod) % mod ;
ans = (ans + 1LL * fdis[u] * t[1].odd % mod * (sum_o - t[1].odd) % mod) % mod ;
}
if(!Kp) Update(u , father) ;
}
int main() {
n = read() ; m = read() ;
for(int i = 1 , u , v , w ; i < n ; i ++) {
u = read() ; v = read() ; w = read() ;
add_edge(u , v , w) ; add_edge(v , u , w) ;
}
for(int i = 1 , x ; i <= m ; i ++) {
x = read() ;
vec[x].push_back(i) ;
}
fdfs(1 , 0) ;
dfs(1 , 0 , 1) ;
printf("%d\n",ans) ;
return 0 ;
}
[UOJ388]配对树的更多相关文章
- 【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)
[UOJ#388][UNR#3]配对树(线段树,dsu on tree) 题面 UOJ 题解 考虑一个固定区间怎么计算答案,把这些点搞下来建树,然后\(dp\),不难发现一个点如果子树内能够匹配的话就 ...
- 【UOJ388】配对树(dsu on tree+线段树)
传送门 题意: 给出一颗含有\(n\)个结点的无根树,之后给出一个长度为\(m\)的序列,每个元素在\([1,n]\)之间. 现在序列中每个长度为偶数的区间的完成时间定义为树上最小配对方法中每对匹配点 ...
- [UOJ388]【UNR #3】配对树
uoj description 给你一棵\(n\)个节点的树以及一个长为\(m\)的序列,序列每个位置上的值\(\in[1,n]\),你需要求出把序列中所有长度为偶数的区间内所有数拿出来在树上以最小代 ...
- uoj#388. 【UNR #3】配对树(线段树合并)
传送门 先考虑一个贪心,对于一条边来说,如果当前这个序列中在它的子树中的元素个数为奇数个,那么这条边就会被一组匹配经过,否则就不会 考虑反证法,如果在这条边两边的元素个数都是偶数,那么至少有两组匹配经 ...
- C. 【UNR #3】配对树
题解: 首先可以贪心 于是问题可以等价成一条边被算当且仅当子树中个数为奇数个 题解的做法比较简单 考虑每条边,加入其子树内的点 然后为了保证区间长度为偶数 分成f0,0 f0,1 f1,0 f1,1即 ...
- Noip前的大抱佛脚----赛前任务
赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...
- UNR#3 Day1——[ 堆+ST表+复杂度分析 ][ 结论 ][ 线段树合并 ]
地址:http://uoj.ac/contest/45 第一题是鸽子固定器. 只会10分.按 s 从小到大排序,然后 dp[ i ][ j ][ k ] 表示前 i 个元素.已经选了 j 个.最小值所 ...
- GZOI 2017配对统计 树状数组
题目 https://www.luogu.com.cn/problem/P5677 分析 最开始读题的时候没有读的太懂,以为i是在选定区间内给的,实际上不是,这道题的意思应该是在l和r的区间内找出有多 ...
- 51Nod 1737 配对(树的重心)
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1737 题意: 思路: 树的重心. 树的重心就是其所以子树的最大的子树结点 ...
随机推荐
- eclipse工程设置项目jre
Eclipse 是一个开放源代码的.基于Java的可扩展开发平台.就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境.当我们导入已经存在项目或者通过svn引入项目时经常出现红叉叉的情 ...
- java SE基础(Collection接口概述)
Collection接口相关集成关系例如以下图 1. 关于可迭代接口(Iterable) 可迭代接口仅包括一个方法,返回一个在一组T类型元素上进行迭代的迭代器: public ...
- 【Mongodb教程 第二课 】 MongoDB 创建数据库 use 命令
use 命令 MongoDB use DATABASE_NAME 用于创建数据库.该命令将创建一个新的数据库,如果它不存在,否则将返回现有的数据库. 语法: use DATABASE 语句的基本语法如 ...
- LoadRunner---http请求中对中文参数的处理
Loadrunner 做保险承保业务测试 1. 保险正常业务流程:保费计算--->保存--->申请核保--->核保--->缴费(出保单) 问题一描述 保费计算接口中,需要把车牌 ...
- 使用python在极坐标中生成一条直线
在测试雷达时,往往需要测试雷达的数据是否准确,这时就需要在雷达图中显示一条标准的直线作为对比. "create a wall" import numpy as np import ...
- [Tue, 11 Aug 2015 ~ Mon, 17 Aug 2015] Deep Learning in arxiv
Image Representations and New Domains inNeural Image Captioning we find that a state-of-theart neura ...
- oracle 10g的备份和还原
采用 expdp备份,impdp还原. 注意这二者不等同于exp和imp.oracle 10g以前,可以采用exp.imp,10g及以后,expdp + impdp矣.据说10g里面,如果采用exp, ...
- C项目实践--家庭财务管理系统
1.功能需求分析 家庭财务管理系统给家庭成员提供了一个管理家庭财务的平台,系统可以对家庭成员的收入和支出进行增加,删除.修改和查询等操作,并能统计总收入和总支出.其主要功能需求描述如下: (1)系统主 ...
- CRM 2011 开发中遇到的问题小结
1.将Retrive 方法改成 RetrieveMultiple时 如果指定的ColumnSet 没有指定主键(entiryname+id),要显示增加实体的主键.否则在调用 Retrieve方法时返 ...
- bzoj3330: [BeiJing2013]分数
口胡 题目hint都给你是一个三分函数了 还不会上三分套三分吗 exp函数又卡 精度又卡 什么sb毒瘤题 浪费时间