【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)
【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)
题面
题解
考虑一个固定区间怎么计算答案,把这些点搞下来建树,然后\(dp\),不难发现一个点如果子树内能够匹配的话就一定会匹配完,所以\(dp\)可以做到线性。
那么根据上面的\(dp\)方式,一条边会被匹配到,当且仅当把这条边删掉之后,两个连通块内分别有奇数个目标点。那么如果我们考虑枚举每一条边,然后把子树内的点给标记一下,于是变成了在原序列上求有多少个偶数区间满足有偶数个点被标记,这个问题可以做一个前缀和,把奇偶位置拆开,于是答案就是两个位置中的奇数个数乘上偶数个数。这个过程可以拿线段树维护。
接下来就变成每次要标记子树中的所有点,每次暴力显然不合理。
改成\(dsu\)这个复杂度就很合理了。这个复杂度似乎是两个\(log\)的。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 100100
#define MOD 998244353
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,m,ans;
struct Line{int v,next,w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int wf[MAX],sz[MAX],hson[MAX];
void dfs1(int u,int ff)
{
sz[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
wf[v]=e[i].w;dfs1(v,u);sz[u]+=sz[v];
if(sz[v]>sz[hson[u]])hson[u]=v;
}
}
#define lson (now<<1)
#define rson (now<<1|1)
struct Node{int s[2][2],r;}t[MAX<<2];
void pushup(int now)
{
t[now].s[0][0]=(t[lson].s[0][0]+t[rson].s[0][0])%MOD;
t[now].s[0][1]=(t[lson].s[0][1]+t[rson].s[0][1])%MOD;
t[now].s[1][0]=(t[lson].s[1][0]+t[rson].s[1][0])%MOD;
t[now].s[1][1]=(t[lson].s[1][1]+t[rson].s[1][1])%MOD;
}
void Build(int now,int l,int r)
{
if(l==r){t[now].s[l&1][0]+=1;return;}
int mid=(l+r)>>1;
Build(lson,l,mid);
Build(rson,mid+1,r);
pushup(now);
}
void putrev(int now)
{
swap(t[now].s[0][0],t[now].s[0][1]);
swap(t[now].s[1][0],t[now].s[1][1]);
t[now].r^=1;
}
void pushdown(int now)
{
if(!t[now].r)return;
putrev(lson);putrev(rson);
t[now].r^=1;
}
void Modify(int now,int l,int r,int L,int R)
{
if(L<=l&&r<=R){putrev(now);return;}
int mid=(l+r)>>1;pushdown(now);
if(L<=mid)Modify(lson,l,mid,L,R);
if(R>mid)Modify(rson,mid+1,r,L,R);
pushup(now);
}
vector<int> V[MAX];
bool vis[MAX];
void upd(int u,int ff)
{
for(int v:V[u])Modify(1,0,m,v,m);
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff&&!vis[e[i].v])upd(e[i].v,u);
}
void dfs(int u,int ff,int tp)
{
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff&&e[i].v!=hson[u])
dfs(e[i].v,u,0);
if(hson[u])dfs(hson[u],u,1),vis[hson[u]]=true;
upd(u,ff);
int cnt=(1ll*t[1].s[0][0]*t[1].s[0][1]+1ll*t[1].s[1][0]*t[1].s[1][1])%MOD;
ans=(ans+1ll*cnt*wf[u])%MOD;
vis[hson[u]]=false;
if(!tp)upd(u,ff);
}
int main()
{
n=read();m=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read(),w=read();
Add(u,v,w);Add(v,u,w);
}
for(int i=1;i<=m;++i)V[read()].push_back(i);
Build(1,0,m);dfs1(1,0);dfs(1,0,0);
printf("%d\n",ans);
return 0;
}
【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)的更多相关文章
- UOJ #164 [清华集训2015]V (线段树)
题目链接 http://uoj.ac/problem/164 题解 神仙线段树题. 首先赋值操作可以等价于减掉正无穷再加上\(x\). 假设某个位置从前到后的操作序列是: \(x_1,x_2,..., ...
- 浅谈树套树(线段树套平衡树)&学习笔记
0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...
- [UOJ UNR#1]奇怪的线段树
来自FallDream的博客,未经允许,请勿转载, 谢谢. 原题可以到UOJ看,传送门 如果存在一个点是白的,却有儿子是黑的,显然无解. 不然的话,只要所有黑色的“黑叶子”节点,即没有黑色的儿子的节点 ...
- UOJ#7. 【NOI2014】购票 | 线段树 凸包优化DP
题目链接 UOJ #7 题解 首先这一定是DP!可以写出: \[f[i] = \min_{ancestor\ j} \{f[j] + (d[j] - d[i]) * p[i] + q[i]\}\] 其 ...
- UOJ #314. 【NOI2017】整数 | 线段树 压位
题目链接 UOJ 134 题解 可爱的电音之王松松松出的题--好妙啊. 首先想一个朴素的做法! 把当前的整数的二进制当作01序列用线段树维护一下(序列的第i位就是整数中位权为\(2^k\)的那一位). ...
- 【uoj#228】基础数据结构练习题 线段树+均摊分析
题目描述 给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有三种:区间加.区间开根.区间求和. $n,m,a_i\le 100000$ . 题解 线段树+均摊分析 对于原来的两个数 $a$ ...
- UOJ#470. 【ZJOI2019】语言 虚树,线段树合并
原文链接www.cnblogs.com/zhouzhendong/p/UOJ470.html 前言 做完情报中心来看这个题突然发现两题有相似之处然后就会做了. 题解 首先,我们考虑将所有答案点对分为两 ...
- UOJ#299. 【CTSC2017】游戏 线段树 概率期望 矩阵
原文链接www.cnblogs.com/zhouzhendong/p/UOJ299.html 前言 不会概率题的菜鸡博主做了一道概率题. 写完发现运行效率榜上的人都没有用心卡常数——矩阵怎么可以用数组 ...
- UOJ#467. 【ZJOI2019】线段树 线段树,概率期望
原文链接www.cnblogs.com/zhouzhendong/p/ZJOI2019Day1T2.html 前言 在LOJ交了一下我的代码,发现它比选手机快将近 4 倍. 题解 对于线段树上每一个节 ...
- 【BZOJ-3306】树 线段树 + DFS序
3306: 树 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 792 Solved: 262[Submit][Status][Discuss] De ...
随机推荐
- Linux网络——配置网络之ifconfig家族命令
Linux网络——配置网络之ifconfig家族命令 摘要:本文主要学习了ifconfig家族用来配置网络的命令. ifconfig命令 ifconfig命令用来显示或设置网络接口信息,设置只是临时生 ...
- Hystrix失败处理逻辑解析
在上篇文章Hystrix工作流程解析中,我们整体介绍了Hystrix的工作流程,知道了Hystrix会在下面四种情况下发生降级: 熔断器打开 线程池/信号量跑满 调用超时 调用失败 本篇文章则介绍一下 ...
- 图片在DIV里边水平垂直居中
图片在一个DIV中要垂直水平居中,首先定义一个DIV .wrap{ width: 600px; height: 400px; border: 1px #000 solid; } 插入图片 <di ...
- 程序员的自我修养系列(一):优雅的代码管理工具之GitHub
1.导言 代码管理是程序员经常遇到一个问题,很多童鞋将代码保存到本地硬盘,此种方法管理混乱,也存在代码丢失的风险,且版本无法控制,因此养成良好的代码管理习惯是程序员的必修课.在众多代码管理工具中笔者在 ...
- Android自定义注解
1.元注解 概念:用来定义其他注解的注解,自定义注解的时候,需要使用它来定义我们的注解. 在jdk 1.5之后提供了 java.lang.annotation 来支持注解功能 常见的四种元 ...
- Saltstack_使用指南11_配置管理-状态之间依赖关系
1. 说明 下文的案例是根据上一篇文章进行的修改.因此请优先读取上一篇文章内容<Saltstack_10_配置管理-状态模块> 并且目录进行了变化,从 /srv/salt/lamp 变为了 ...
- BayaiM__Oracle ASM操作管理
BayaiM__Oracle ASM操作管理 BayaiM__Oracle ASM操作管理 ...
- Python—路由追踪(并生成追踪图片)
需要先安装两个包 [root@localhost ~]# yum install graphviz // 为了使用dot命令 [root@localhost ~]# yum install Image ...
- Python中线程的使用
并发:多个任务同一时间段进行 并行:多个任务同一时刻进行 线程的实现 线程模块 Python通过两个标准库_thread 和threading,提供对线程的支持 , threading对_thread ...
- Shell命令-用户用户组管理之userdel、groupadd
文件及内容处理 - userdel.groupadd 1. userdel:删除用户 userdel命令的功能说明 userdel 命令用于删除用户帐号.userdel 可删除用户帐号与相关的文件.若 ...