BZOJ

洛谷

\(dsu\ on\ tree\)。(线段树合并的做法也挺显然不写了)

如果没写过\(dsu\)可以看这里

对修改操作做一下差分放到对应点上,就成了求每个点子树内出现次数最多的颜色,这就和CF600E类似了。直接用\(dsu\)。

修改某个颜色出现次数的时候,最大值不能\(O(1)\)求出,得套个\(set\),所以复杂度是\(O(q\log^2n)\)。但常数并不大。

关于复杂度,在CF600E中对一个点的修改是\(O(1)\)的,而本题中可能是\(O(q)\)(一个点上挂着很多次修改)。但\(dsu\)的复杂度保证在于,每个点只会被统计\(O(\log n)\)次,所以不算\(set\)复杂度依旧是\(O(q\log n)\)。

一些细节:

计算轻儿子子树贡献的时候必须自叶子向上更新,否则在中间会出现某种颜色出现次数\(<0\)的情况导致RE...(因为差分的减标记在上面,加标记在底层);

对子树的DFS可以通过枚举DFS序代替,应该能优化不少常数。

另外BZOJ上z的数据范围其实只有1e5,所以不用离散化233.

//24736kb	4284ms
#include <set>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5; int dep[N],fa[N],sz[N],son[N],top[N],L[N],R[N],A[N],Max,tm[N],cnt[N],Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;
std::set<int> st[N];
struct Graph
{
int Enum,H[N],nxt[N<<2],to[N<<2];
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline void AQ(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
}
}T,Q; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline int LCA(int u,int v)
{
while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
return dep[u]<dep[v]?u:v;
}
void DFS1(int x)
{
int mx=0; sz[x]=1;
for(int i=T.H[x],v; i; i=T.nxt[i])
if((v=T.to[i])!=fa[x])
fa[v]=x, dep[v]=dep[x]+1, DFS1(v), sz[x]+=sz[v], sz[v]>mx&&(mx=sz[v],son[x]=v);
}
void DFS2(int x,int tp)
{
static int Index=0;
A[L[x]=++Index]=x, top[x]=tp;
if(son[x])
{
DFS2(son[x],tp);
for(int i=T.H[x],v; i; i=T.nxt[i])
if((v=T.to[i])!=fa[x] && v!=son[x]) DFS2(v,v);
}
R[x]=Index;
}
inline void Add(int c)
{
int t=tm[c]; ++tm[c];
--cnt[t], ++cnt[t+1], st[t].erase(c), st[t+1].insert(c);
if(t+1>tm[Max]||(t+1==tm[Max] && c<Max)) Max=c;
}
inline void Subd(int c)
{
int t=tm[c]; --tm[c];
--cnt[t], ++cnt[t-1], st[t].erase(c), st[t-1].insert(c);
if(c==Max) !cnt[t] ? Max=*st[t-1].begin() : Max=*st[t].begin();
}
inline void Upd(int x)
{
for(int i=Q.H[x]; i; i=Q.nxt[i])
i&1 ? Add(Q.to[i]) : Subd(Q.to[i]);
}
void Solve(int x,bool keep)
{
static int Time,vis[N];
for(int i=T.H[x],v; i; i=T.nxt[i])
if((v=T.to[i])!=fa[x] && v!=son[x]) Solve(v,0);
if(son[x]) Solve(son[x],1); for(int i=T.H[x],v; i; i=T.nxt[i])
if((v=T.to[i])!=fa[x] && v!=son[x])
for(int j=R[v]; j>=L[v]; --j) Upd(A[j]);//要倒序枚举
Upd(x), Ans[x]=tm[Max]?Max:0; if(!keep)
{
Max=0, ++Time;
for(int u=L[x]; u<=R[x]; ++u)
for(int i=Q.H[A[u]]; i; i=Q.nxt[i])
{
int c=Q.to[i];
if(vis[c]!=Time) vis[c]=Time, --cnt[tm[c]], st[tm[c]].erase(c), ++cnt[tm[c]=0];//, st[0].insert(c);//erase不存在的元素没有问题
}
}
} int main()
{
int n=read(),m=read();
for(int i=1; i<n; ++i) T.AE(read(),read());
DFS1(1), DFS2(1,1);
for(int i=1; i<=m; ++i)
{
int x=read(),y=read(),z=read(),w=LCA(x,y);
Q.AQ(x,z), Q.AQ(w,z), Q.AQ(y,z), Q.AQ(fa[w],z);
}
Max=0, cnt[0]=n, Solve(1,1);
for(int i=1; i<=n; ++i) printf("%d\n",Ans[i]); return 0;
}

BZOJ.3307.雨天的尾巴(dsu on tree/线段树合并)的更多相关文章

  1. [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)

    [BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...

  2. bzoj3307雨天的尾巴(权值线段树合并/DSU on tree)

    题目大意: 一颗树,想要在树链上添加同一物品,问最后每个点上哪个物品最多. 解题思路: 1.线段树合并 假如说物品数量少到可以暴力添加,且树点极少,我们怎么做. 首先在一个树节点上标记出哪些物品有多少 ...

  3. luoguP4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (线段树-权值-动态开点,树链剖分)

    中学毕业了,十七号就要前往武汉报道.中学的终点是武汉大学,人生的终点却不是,最初的热情依然失却,我还是回来看看这分类排版皆惨淡的博客吧,只是是用来保存代码也好.想要换一个新博客,带着之前的经验能把它整 ...

  4. [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...

  5. 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...

  6. P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)

    显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...

  7. BZOJ 3307: 雨天的尾巴( LCA + 线段树合并 )

    路径(x, y) +z : u处+z, v处+z, lca(u,v)处-z, fa(lca)处-z, 然后dfs一遍, 用线段树合并. O(M log M + M log N). 复杂度看起来不高, ...

  8. Bzoj 3307 雨天的尾巴(线段树合并+树上差分)

    C. 雨天的尾巴 题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入格式 第 ...

  9. BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)

    题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...

随机推荐

  1. Java SimpleDateFormat 中英文时间格式化转换

    2015年08月29日 17:37:43 阅读数:32459 SimpleDateFormat是一个以与语言环境有关的方式来格式化和解析日期的具体类.它允许进行格式化(日期 -> 文本).解析( ...

  2. const 和 const_cast

    对于const变量,我们不能修改它的值,这是这个限定符最直接的表现.但是我们就是想违背它的限定希望修改其内容怎么办呢?下边的代码显然是达不到目的的: ; int modifier = constant ...

  3. 饮冰三年-人工智能-Python-15Python基础之文件处理

    # 写文件--开始 f=open('test','w',encoding="utf-8") #如果有则写,没有就创建 print(f.writable()) #True f.wri ...

  4. 【C++ Primer | 03】字符串、向量和数组

    博客链接: c++ 中 const_iterator 和 const vector<>::iterator的区别 const vector <int> ::iterator和v ...

  5. android app使用微信登录接口回调没有被执行的问题研究

    本人开发的一个app使用了sharesdk集成微信登录功能,在测试的过程中微信授权登录界面有调用,但是授权后原应用的回调没有被执行 应用的包名是com.kimi.searcher 首先,确认微信点击授 ...

  6. Nancy 寄宿OWin

    一:创建一个空项目 二:安装模板 Install-Package Nancy Install-Package Nancy.Owin Install-Package Microsoft.Owin ins ...

  7. __attribute__ ((default)) 和 __attribute__ ((hidden))

    制作一个共享库 /* a.h */ int func(); /* a.c */ #include <stdio.h> #include "a.h" int func() ...

  8. json 解析代码 全语言

    Javascript: 1.使用eval var parse_json_by_eval = function(str){ return eval('('+str+')'); } var value = ...

  9. c_数据结构_栈的实现

    #include<stdio.h> #include<stdlib.h> #define STACK_INIT_SIZE 100 #define STACKINCREMENT ...

  10. Filebeat6.31整合Kafka集群消息队列(三)

    wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.3.2-linux-x86_64.tar.gz [root@ ...