传送门

先考虑一个贪心,对于一条边来说,如果当前这个序列中在它的子树中的元素个数为奇数个,那么这条边就会被一组匹配经过,否则就不会

考虑反证法,如果在这条边两边的元素个数都是偶数,那么至少有两组匹配经过它,那么把这两条路径都删去这条边可以更优。如果两边是奇数,一定至少有一条路径经过它,去掉这组匹配之后就变成了偶数的情况。证毕

然后是一个神仙的转化,我们对于一颗子树中的元素,在序列里标记为\(1\),否则为\(0\),那么这条边出现次数就是序列中长度为偶数且区间和为奇数的区间个数

考虑用线段树合并优化,对于每个节点,记\(t[p][0/1][0/1]\)表示节点\(p\)代表的区间中前缀和为偶数\(/\)奇数,下标为偶数\(/\)奇数的下标个数,然后线段树合并就行了

然而咱还是搞不明白为啥线段树上的区间要设为\([1,m+1]\)……有哪位知道为什么的请告诉咱一声……

  1. //minamoto
  2. #include<bits/stdc++.h>
  3. #define R register
  4. #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
  5. #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
  6. #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
  7. using namespace std;
  8. char buf[1<<21],*p1=buf,*p2=buf;
  9. inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
  10. int read(){
  11. R int res,f=1;R char ch;
  12. while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
  13. for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
  14. return res*f;
  15. }
  16. const int N=1e5+5,M=N<<5,P=998244353;
  17. inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
  18. inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
  19. inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
  20. int ksm(R int x,R int y){
  21. R int res=1;
  22. for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
  23. return res;
  24. }
  25. struct eg{int v,nx,w;}e[N<<1];int head[N],tot;
  26. inline void add_edge(R int u,R int v,R int w){e[++tot]={v,head[u],w},head[u]=tot;}
  27. int sum[M],ls[M],rs[M],t[M][2][2],rt[N];
  28. int n,m,ans,cnt,u,v,w;
  29. void upd(int p,int l,int r){
  30. sum[p]=0;
  31. if(ls[p])sum[p]+=sum[ls[p]];
  32. if(rs[p])sum[p]+=sum[rs[p]];
  33. int x=ls[p]?sum[ls[p]]&1:0;
  34. fp(i,0,1)fp(j,0,1){
  35. t[p][i][j]=0;
  36. if(ls[p])t[p][i][j]+=t[ls[p]][i][j];
  37. if(rs[p])t[p][i][j]+=t[rs[p]][i^x][j];
  38. }
  39. int mid=(l+r)>>1;
  40. if(!ls[p])t[p][0][0]+=(mid>>1)-((l-1)>>1),t[p][0][1]+=((mid+1)>>1)-(l>>1);
  41. if(!rs[p])t[p][x][0]+=(r>>1)-(mid>>1),t[p][x][1]+=((r+1)>>1)-((mid+1)>>1);
  42. }
  43. void ins(int &p,int l,int r,int x){
  44. if(!p){
  45. p=++cnt;
  46. t[p][0][0]=(r>>1)-((l-1)>>1);
  47. t[p][0][1]=((r+1)>>1)-(l>>1);
  48. }
  49. if(l==r)return ++sum[p],void();
  50. int mid=(l+r)>>1;
  51. x<=mid?ins(ls[p],l,mid,x):ins(rs[p],mid+1,r,x);
  52. upd(p,l,r);
  53. }
  54. int merge(int x,int y,int l,int r){
  55. if(!x||!y)return x|y;
  56. int mid=(l+r)>>1;
  57. ls[x]=merge(ls[x],ls[y],l,mid);
  58. rs[x]=merge(rs[x],rs[y],mid+1,r);
  59. upd(x,l,r);
  60. return x;
  61. }
  62. void dfs(int u,int fa){
  63. go(u)if(v!=fa){
  64. dfs(v,u);
  65. ans=add(ans,mul(e[i].w,1ll*t[rt[v]][0][0]*t[rt[v]][1][0]%P+1ll*t[rt[v]][0][1]*t[rt[v]][1][1]%P));
  66. rt[u]=merge(rt[u],rt[v],1,m+1);
  67. }
  68. }
  69. int main(){
  70. // freopen("testdata.in","r",stdin);
  71. n=read(),m=read();
  72. fp(i,1,n-1)u=read(),v=read(),w=read(),add_edge(u,v,w),add_edge(v,u,w);
  73. fp(i,1,m)u=read(),ins(rt[u],1,m+1,i);
  74. dfs(1,0);
  75. printf("%d\n",ans);
  76. return 0;
  77. }

uoj#388. 【UNR #3】配对树(线段树合并)的更多相关文章

  1. UOJ #164 [清华集训2015]V (线段树)

    题目链接 http://uoj.ac/problem/164 题解 神仙线段树题. 首先赋值操作可以等价于减掉正无穷再加上\(x\). 假设某个位置从前到后的操作序列是: \(x_1,x_2,..., ...

  2. 浅谈树套树(线段树套平衡树)&学习笔记

    0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...

  3. [UOJ UNR#1]奇怪的线段树

    来自FallDream的博客,未经允许,请勿转载, 谢谢. 原题可以到UOJ看,传送门 如果存在一个点是白的,却有儿子是黑的,显然无解. 不然的话,只要所有黑色的“黑叶子”节点,即没有黑色的儿子的节点 ...

  4. UOJ#7. 【NOI2014】购票 | 线段树 凸包优化DP

    题目链接 UOJ #7 题解 首先这一定是DP!可以写出: \[f[i] = \min_{ancestor\ j} \{f[j] + (d[j] - d[i]) * p[i] + q[i]\}\] 其 ...

  5. UOJ #314. 【NOI2017】整数 | 线段树 压位

    题目链接 UOJ 134 题解 可爱的电音之王松松松出的题--好妙啊. 首先想一个朴素的做法! 把当前的整数的二进制当作01序列用线段树维护一下(序列的第i位就是整数中位权为\(2^k\)的那一位). ...

  6. 【uoj#228】基础数据结构练习题 线段树+均摊分析

    题目描述 给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有三种:区间加.区间开根.区间求和. $n,m,a_i\le 100000$ . 题解 线段树+均摊分析 对于原来的两个数 $a$ ...

  7. UOJ#470. 【ZJOI2019】语言 虚树,线段树合并

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ470.html 前言 做完情报中心来看这个题突然发现两题有相似之处然后就会做了. 题解 首先,我们考虑将所有答案点对分为两 ...

  8. UOJ#299. 【CTSC2017】游戏 线段树 概率期望 矩阵

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ299.html 前言 不会概率题的菜鸡博主做了一道概率题. 写完发现运行效率榜上的人都没有用心卡常数——矩阵怎么可以用数组 ...

  9. UOJ#467. 【ZJOI2019】线段树 线段树,概率期望

    原文链接www.cnblogs.com/zhouzhendong/p/ZJOI2019Day1T2.html 前言 在LOJ交了一下我的代码,发现它比选手机快将近 4 倍. 题解 对于线段树上每一个节 ...

  10. 【BZOJ-3306】树 线段树 + DFS序

    3306: 树 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 792  Solved: 262[Submit][Status][Discuss] De ...

随机推荐

  1. vi中如何替换某字符成“回车”?

    vi中如何替换某字符成“回车”? 在 vi 中::s/,/^M/g (you need to type CTRL-V <CR> to get a ^M here)VIM - Vi IMpr ...

  2. JETSON TK1~Ubuntu14.04 Armhf源更新

    Ubuntu armhf版本的源网址不同于普通Ubuntu系统,如果采用如下网址会出现问题,导致sudo apt-get update出现Error. 之前的连接: deb http://archiv ...

  3. 微信小程序开发:学习笔记[4]——样式布局

    微信小程序开发:学习笔记[4]——样式布局 Flex布局 新的布局方式 在小程序开发中,我们需要考虑各种尺寸终端设备上的适配.在传统网页开发,我们用的是盒模型,通过display:inline | b ...

  4. java.util.ResourceBundle国际化用法详解

    java.util.ResourceBundle国际化用法详解 初识国际化和ResourceBundle 这个类主要用来解决国际化和本地化问题.国际化和本地化可不是两个概念,两者都是一起出现的.可以说 ...

  5. Algorithm: dynamic programming

    1. Longest Increasing Subsequence (LIS) problem unsorted array, calculate out the maximum length of ...

  6. Java多线程系列 基础篇07 synchronized底层优化

    转载 http://www.cnblogs.com/paddix/ 作者:liuxiaopeng http://www.infoq.com/cn/articles/java-se-16-synchro ...

  7. 常用的.gitignore文件

    target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project ...

  8. Android Weekly Notes Issue #255

    Android Weekly Issue #255 April 30th, 2017 Android Weekly Issue #255 本期内容包括: 一种在RxJava中显示loading/con ...

  9. POJ 1330 Nearest Common Ancestors 【最近公共祖先LCA算法+Tarjan离线算法】

    Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20715   Accept ...

  10. poj 3041 Asteroids(二分图 *【矩阵实现】【最小点覆盖==最大匹配数】)

    Asteroids Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 16379   Accepted: 8930 Descri ...