uoj

description

给你一棵\(n\)个节点的树以及一个长为\(m\)的序列,序列每个位置上的值\(\in[1,n]\),你需要求出把序列中所有长度为偶数的区间内所有数拿出来在树上以最小代价匹配的代价之和模\(998244353\)。

sol

首先拿出偶数个点在树上匹配这个问题,根据贪心,我们一定会让这些点在尽可能深的位置匹配。换句话说,每棵子树中未匹配的点至多只有一个。

那么我们考虑每一条边,这一条边会被计算贡献当且仅当这条边连接的子树里有奇数个选出的点。

那么我们相当于是要求对于每一条边,有多少个长度为偶数的区间使得这条边连接的子树里有奇数个区间内的点。

考虑把这棵子树里的点在原序列中标记为\(1\),其他点标记为\(0\),然后对这个序列做一遍前缀和。

那么我们要求的东西实际上就是,满足\(i \equiv j \pmod 2,s_j-s_i\equiv1\pmod 2\)的\((i,j)\)对数,这相当于是区间\([i+1,j]\)的和为奇数。注意这里的\(i,j\)可以取\(0\)。

然后我们就只需要对每一棵子树维护出这个东西的数量就好啦。

暴力的话就是暴\(for\)子树里的每一个点,可以修改原序列的单点,也就是给前缀和的一段后缀\(+1\),用线段树维护每个区间有多少个下标是奇数/偶数的点的前缀和是奇数,修改就相当于区间翻转,复杂度\(O(n^2\log m)\)。

然后就\(dsu\ on\ tree\)一波,复杂度变成了\(O(n\log n\log m)\)。

其实直接线段树合并就\(O(n\log m)\)了,但是懒得写了qaq。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e5+5;
const int mod = 998244353;
int n,m,to[N<<1],nxt[N<<1],ww[N<<1],head[N],cnt,pre[N],fst[N];
int dis[N],sz[N],son[N],rev[N<<2],ans;
struct data{
int od,en;
data operator + (const data &b) const{
return (data){od+b.od,en+b.en};
}
}t[N<<2];
void link(int u,int v,int w){
to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=w;head[u]=cnt;
}
void reverse(int x,int l,int r){
t[x].od=(r+1)/2-l/2-t[x].od;
t[x].en=r/2-(l-1)/2-t[x].en;
rev[x]^=1;
}
void pushdown(int x,int l,int r){
if (!rev[x]) return;int mid=l+r>>1;
reverse(x<<1,l,mid);reverse(x<<1|1,mid+1,r);
rev[x]=0;
}
void modify(int x,int l,int r,int ql,int qr){
if (l>=ql&&r<=qr) {reverse(x,l,r);return;}
pushdown(x,l,r);int mid=l+r>>1;
if (ql<=mid) modify(x<<1,l,mid,ql,qr);
if (qr>mid) modify(x<<1|1,mid+1,r,ql,qr);
t[x]=t[x<<1]+t[x<<1|1];
}
void dfs_pre(int u,int f){
sz[u]=1;
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f){
dis[to[e]]=ww[e],dfs_pre(to[e],u),sz[u]+=sz[to[e]];
if (sz[to[e]]>sz[son[u]]) son[u]=to[e];
}
}
void add(int u){
for (int i=fst[u];i;i=pre[i]) modify(1,1,m,i,m);
}
void cal(int u){
int tim=(1ll*t[1].od*((m+1)/2-t[1].od)+1ll*t[1].en*(m/2+1-t[1].en))%mod;
ans=(1ll*dis[u]*tim+ans)%mod;
}
void upt(int u,int f){
add(u);
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f) upt(to[e],u);
}
void dfs(int u,int f,bool keep){
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f&&to[e]!=son[u])
dfs(to[e],u,0);
if (son[u]) dfs(son[u],u,1);
for (int e=head[u];e;e=nxt[e])
if (to[e]!=f&&to[e]!=son[u]) upt(to[e],u);
add(u);cal(u);
if (!keep) upt(u,f);
}
int main(){
n=gi();m=gi();
for (int i=1;i<n;++i){
int u=gi(),v=gi(),w=gi();
link(u,v,w);link(v,u,w);
}
for (int i=1;i<=m;++i){
int v=gi();pre[i]=fst[v];fst[v]=i;
}
dfs_pre(1,0);dfs(1,0,1);printf("%d\n",ans);return 0;
}

[UOJ388]【UNR #3】配对树的更多相关文章

  1. 【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)

    [UOJ#388][UNR#3]配对树(线段树,dsu on tree) 题面 UOJ 题解 考虑一个固定区间怎么计算答案,把这些点搞下来建树,然后\(dp\),不难发现一个点如果子树内能够匹配的话就 ...

  2. 【UOJ388】配对树(dsu on tree+线段树)

    传送门 题意: 给出一颗含有\(n\)个结点的无根树,之后给出一个长度为\(m\)的序列,每个元素在\([1,n]\)之间. 现在序列中每个长度为偶数的区间的完成时间定义为树上最小配对方法中每对匹配点 ...

  3. uoj#388. 【UNR #3】配对树(线段树合并)

    传送门 先考虑一个贪心,对于一条边来说,如果当前这个序列中在它的子树中的元素个数为奇数个,那么这条边就会被一组匹配经过,否则就不会 考虑反证法,如果在这条边两边的元素个数都是偶数,那么至少有两组匹配经 ...

  4. [UOJ388]配对树

    题解 贪心+线段树 首先如果我们知道了哪些点是关键点应该怎么搞 显然最小的匹配方案所有的边至多被经过一次 可以考虑每条边的贡献 因为我们要贡献尽量小 所以我们尽量让每条边经过的人尽量少 那么每条边被经 ...

  5. C. 【UNR #3】配对树

    题解: 首先可以贪心 于是问题可以等价成一条边被算当且仅当子树中个数为奇数个 题解的做法比较简单 考虑每条边,加入其子树内的点 然后为了保证区间长度为偶数 分成f0,0 f0,1 f1,0 f1,1即 ...

  6. UNR#3 Day1——[ 堆+ST表+复杂度分析 ][ 结论 ][ 线段树合并 ]

    地址:http://uoj.ac/contest/45 第一题是鸽子固定器. 只会10分.按 s 从小到大排序,然后 dp[ i ][ j ][ k ] 表示前 i 个元素.已经选了 j 个.最小值所 ...

  7. Noip前的大抱佛脚----赛前任务

    赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...

  8. 【UNR #1】火车管理(主席树)

    [UNR #1]火车管理(主席树) 好好的代码被 \(extra\ test\) 卡常了...我就放一个目前最快的版本吧... 题意简化: 有 \(n\) 个栈,\(m\) 次操作. 将 \(x\) ...

  9. 「UNR#1」奇怪的线段树

    「UNR#1」奇怪的线段树 一道好题,感觉解法非常自然. 首先我们只需要考虑一次染色最下面被包含的那些区间,因为把无解判掉以后只要染了一个节点,它的祖先也一定被染了.然后发现一次染色最下面的那些区间一 ...

随机推荐

  1. vuex的一个坑

    1  error in callback for watcher "function (){ return this._data.$$state }" 用深拷贝解决 2 接口依赖: ...

  2. 前端优化点(此文转载 http://mp.weixin.qq.com/s/6mVVKmqDL_xYl15AeoJTWg)

    此文转载自:http://mp.weixin.qq.com/s/6mVVKmqDL_xYl15AeoJTWg (原文地址) 围绕前端的性能多如牛毛,涉及到方方面面,以下我们将围绕PC浏览器和移动端浏览 ...

  3. java -- JVM的符号引用和直接引用

    在JVM中类加载过程中,在解析阶段,Java虚拟机会把类的二级制数据中的符号引用替换为直接引用. 1.符号引用(Symbolic References): 符号引用以一组符号来描述所引用的目标,符号可 ...

  4. 项目中使用protobuf 3.0

    protocol buffer从3.0 原生的compiler支持c++,Java,Python,Go,Ruby,JavaNano,JavaScript,Objective-C,C#,PHP这篇文章作 ...

  5. 用opencv检测人眼并定位瞳孔位置

    最近的研究要用到定位瞳孔的位置,所以上网搜了下相关的代码.总结如下: 1) 定位瞳孔可以直接使用opencv中的自带的分类器(haarcascade_eye_tree_eyeglasses.xml)来 ...

  6. Putty出现 Network error:Software caused connection abort

    使用centos7.5 用Putty连接使用没多久就会出现 Network error:Software caused connection abort #修改sshd配置文件.修改3项配置即可 vi ...

  7. 20145105 《Java程序设计》实验一总结

    实验一   Java开发环境的熟悉 一.    实验内容: (一)使用JDK编译.运行简单的程序 (二)使用idea编辑.编译.运行.调试Java程序. 二.    实验步骤: (一)   命令行下J ...

  8. 20145106 《Java程序设计》第1周学习总结

    20145106 <Java程序设计>第1周学习总结 教材学习内容总结 因为我用的是Mac,所以教材内容暂时对我的编译java没有帮助.不过还好我也在同学和自己的帮助学习下初步学会了在Ma ...

  9. 20145307陈俊达《网络对抗》Exp4 恶意代码分析

    20145307陈俊达<网络对抗>Exp4 恶意代码分析 基础问题回答 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪 ...

  10. Linux内核分析08

    进程的切换和系统的一般执行过程 一,进程切换的关键代码switch_to分析 进程调度的时机 中断处理过程(包括时钟中断.I/O中断.系统调用和异常)中,直接调用schedule(),或者返回用户态时 ...