bzoj3307雨天的尾巴(可持久化线段树合并)

题目大意:

一颗树,想要在树链上添加同一物品,问最后每个点上哪个物品最多。

解题思路:

假如说物品数量少到可以暴力添加,且树点极少,我们怎么做。

首先在一个树节点上标记出哪些物品有多少,寻找道路上的节点暴力添加。

而如果节点比较多,我们使用树上差分u+1,v+1,lca-1,fa[lca]-1向上求和就得到了答案

而颜色较多呢,和刚才唯一不同就在于向上求和时不要用数组,用线段树合并就好了(记录线段树区间的最大值与最大位置)

在神犇博客那里看到了废点的回收^_^

代码:

  1 #include<queue>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #define lll tr[spc].ls
  6 #define rrr tr[spc].rs
  7 const int maxin=1000000000;
  8 struct pnt{
  9     int fa[20];
 10     int hd;
 11     int gd;
 12     int dp;
 13     int wgt;
 14     int ans;
 15     int prp;
 16     int root;
 17 }p[110000];
 18 struct ent{
 19     int twd;
 20     int lst;
 21 }e[220000];
 22 struct ljt{
 23     int twd;
 24     int lst;
 25     int vls;
 26 }el[420000];
 27 struct trnt{
 28     int ls;
 29     int rs;
 30     int val;
 31     int mpl;
 32 }tr[2000000],org;
 33 int bin[2000000];
 34 int top;
 35 int siz;
 36 int cnt1;
 37 int cnt2;
 38 int n,m;
 39 void adde(int f,int t)
 40 {
 41     cnt1++;
 42     e[cnt1].twd=t;
 43     e[cnt1].lst=p[f].hd;
 44     p[f].hd=cnt1;
 45     return ;
 46 }
 47 void addc(int pt,int c,int v)
 48 {
 49     cnt2++;
 50     el[cnt2].twd=c;
 51     el[cnt2].lst=p[pt].gd;
 52     el[cnt2].vls=v;
 53     p[pt].gd=cnt2;
 54     return ;
 55 }
 56 namespace Sgt{
 57     void P_delete(int spc)
 58     {
 59         bin[++top]=spc;
 60         tr[spc]=org;
 61         return ;
 62     }
 63     void P_destory(int spc1,int spc2)
 64     {
 65         P_delete(spc1);
 66         P_delete(spc2);
 67         return ;
 68     }
 69     int P_new(void)
 70     {
 71         if(top)
 72             return bin[top--];
 73         return ++siz;
 74     }
 75     void pushup(int spc)
 76     {
 77         if(tr[lll].val>=tr[rrr].val)
 78             tr[spc].val=tr[lll].val,tr[spc].mpl=tr[lll].mpl;
 79         else
 80             tr[spc].val=tr[rrr].val,tr[spc].mpl=tr[rrr].mpl;
 81         return ;
 82     }
 83     int P_merge(int spc1,int spc2,int l,int r)
 84     {
 85         if(!spc1||!spc2)
 86             return spc1+spc2;
 87         int spc=P_new();
 88         if(l==r)
 89         {
 90             tr[spc].val=tr[spc1].val+tr[spc2].val;
 91             tr[spc].mpl=l;
 92             P_destory(spc1,spc2);
 93             return spc;
 94         }
 95         int mid=(l+r)>>1;
 96         tr[spc].ls=P_merge(tr[spc1].ls,tr[spc2].ls,l,mid);
 97         tr[spc].rs=P_merge(tr[spc1].rs,tr[spc2].rs,mid+1,r);
 98         pushup(spc);
 99         P_destory(spc1,spc2);
100         return spc;
101     }
102     void Update(int l,int r,int &spc,int pos,int v)
103     {
104         if(!spc)
105             spc=P_new();
106         if(l==r)
107         {
108             tr[spc].val+=v;
109             tr[spc].mpl=(tr[spc].val)?l:0;
110             return ;
111         }
112         int mid=(l+r)>>1;
113         if(pos<=mid)
114             Update(l,mid,tr[spc].ls,pos,v);
115         else
116             Update(mid+1,r,tr[spc].rs,pos,v);
117         pushup(spc);
118         if(!tr[spc].val)
119             tr[spc].mpl=0;
120         return ;
121     }
122 }
123 void Basic_dfs(int x,int f)
124 {
125     p[x].dp=p[f].dp+1;
126     p[x].fa[0]=f;
127     for(int i=1;i<=19;i++)
128         p[x].fa[i]=p[p[x].fa[i-1]].fa[i-1];
129     p[x].wgt=1;
130     for(int i=p[x].hd;i;i=e[i].lst)
131     {
132         int to=e[i].twd;
133         if(to==f)
134             continue;
135         Basic_dfs(to,x);
136         p[x].wgt+=p[to].wgt;
137     }
138     return ;
139 }
140 void Ans_dfs(int x,int f)
141 {
142     for(int i=p[x].hd;i;i=e[i].lst)
143     {
144         int to=e[i].twd;
145         if(to==f)
146             continue;
147         Ans_dfs(to,x);
148     }
149     for(int i=p[x].gd;i;i=el[i].lst)
150     {
151         int clr=el[i].twd;
152         Sgt::Update(1,maxin,p[x].root,clr,el[i].vls);
153     }
154     p[x].ans=tr[p[x].root].mpl;
155     p[f].root=Sgt::P_merge(p[f].root,p[x].root,1,maxin);
156 }
157 int Lca(int x,int y)
158 {
159     if(p[x].dp<p[y].dp)
160         std::swap(x,y);
161     for(int i=19;i>=0;i--)
162         if(p[p[x].fa[i]].dp>=p[y].dp)
163             x=p[x].fa[i];
164     if(x==y)
165         return x;
166     for(int i=19;i>=0;i--)
167         if(p[x].fa[i]!=p[y].fa[i])
168             x=p[x].fa[i],y=p[y].fa[i];
169     return p[x].fa[0];
170 }
171 int main()
172 {
173     scanf("%d%d",&n,&m);
174     for(int i=1;i<n;i++)
175     {
176         int a,b;
177         scanf("%d%d",&a,&b);
178         adde(a,b);
179         adde(b,a);
180     }
181     Basic_dfs(1,1);
182     for(int i=1;i<=m;i++)
183     {
184         int x,y,z;
185         scanf("%d%d%d",&x,&y,&z);
186         addc(x,z,1);
187         addc(y,z,1);
188         int lca=Lca(x,y);
189         addc(lca,z,-1);
190         if(lca!=1)
191             addc(p[lca].fa[0],z,-1);
192     }
193     Ans_dfs(1,1);
194     for(int i=1;i<=n;i++)
195         printf("%d\n",p[i].ans);
196     return 0;
197 }

原文地址:https://www.cnblogs.com/blog-Dr-J/p/9589142.html

时间: 2024-10-08 14:45:20

bzoj3307雨天的尾巴(可持久化线段树合并)的相关文章

[BZOJ 3551] Peaks 半可持久化并查集 可持久化线段树合并

实现 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include <algorithm> 6 #include <map> 7 using namespace std; 8 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 9 inl

HDU - 4358 Boring counting (树上启发式合并/线段树合并)

题目链接 题意:统计树上每个结点中恰好出现了k次的颜色数. dsu on tree/线段树合并裸题. 启发式合并1:(748ms) 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10; 5 int n,m,k,a[N],b[N],nb,fa[N],son[N],siz[N],cnt[N],ans[N],now,ne,hd[N],ka; 6 struct E {

[模板] 线段树合并

线段树合并 把若干棵叶子节点总数为 \(n\) 的线段树通过某种顺序合并成一棵线段树. 时间复杂度 \(O(n \log n)\). 时间复杂度分析 考虑两颗线段树合并, 复杂度为这两颗线段树的相同节点个数. 这可以看作是删除的节点个数. 那么所有线段树合并, 所有节点最多被删除一次. 时间复杂度即为 \(O(n \log n)\). 线段树合并的空间复杂度也为 \(O(n \log n)\), 而可持久化的线段树合并 (见下) 使用的空间不超过 \(2 \cdot n\log n\). 普通的

P4556 [Vani有约会]雨天的尾巴 树链剖分 线段树合并

P4556 [Vani有约会]雨天的尾巴 提交2.75k 通过789 时间限制1.00s 内存限制125.00MB 提交代码加入收藏 题目提供者yyy2015c01 难度省选/NOI- 历史分数100 提交记录查看题解 标签 查看算法标签 相关讨论 进入讨论版 查看讨论 推荐题目 查看推荐 展开 题目背景 深绘里一直很讨厌雨天.灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切.虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮

[树上差分][线段树合并]JZOJ 3397 雨天的尾巴

Description 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连 根拔起,以及田地里的粮食被弄得一片狼藉. 无奈的深绘里和村民们只好等待救济粮来维生. 不过救济粮的发放方式很特别. 首先村落里的一共有n 座房屋,并形成一个树状结构.然后救济粮分m 次发放,每次选择 两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮.

P4556 [Vani有约会]雨天的尾巴(线段树合并)

传送门 一道线段树合并 首先不难看出树上差分 我们把每一次修改拆成四个,在\(u,v\)分别放上一个,在\(lca\)和\(fa[lca]\)各减去一个,那么只要统计一下子树里的总数即可 然而问题就在于怎么统计.直接暴力肯定是要咕咕的,那么线段树合并就派上用场了 总之就是每个点开一个动态开点线段树,然后一遍dfs,让它的所有儿子的线段树合并到它这里 我按以前的写法不知为什么写挂了--然后换抄了种写法还是挂--后来发现是写抄的时候没有注意合并的顺序-- //minamoto #include<bi

Luogu4556 雨天的尾巴 树上差分、线段树合并

传送门 一个套路题-- 树上差分+线段树合并即可 注意一个细节:$pushup$的时候如果最大值为$0$一定要把最大值对应的答案也设为$0$,否则会$WA$第二个点 另外如果这道题空限变小了请不要交这份代码,因为这份代码没有卡空间... 1 #include<bits/stdc++.h> 2 #define mid ((l + r) >> 1) 3 //This code is written by Itst 4 using namespace std; 5 6 inline in

P4556 雨天的尾巴 线段树合并

使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标).所以每个节点答案即为\(tre[rot[x]]\). 然后运用树上点的差分思想,对于分发路径\(u,v\),我们在\(u\)上+1,在\(v\)+1,在\(lca(u,v)\)处-1,在\(fa(lca)\)处-1,最后统计时自底向上做树上前缀和.线段树合并即得当前节点信息. 需要注意的是,在合并时可能会出现\(tre[rot[x]]\)不为\(0\),但是\(sum[rot[x]]\)为\(0\

bzoj3307: 雨天的尾巴

3307: 雨天的尾巴 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 479  Solved: 214[Submit][Status][Discuss] Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. Input 第一行数字N,M接下来N-1行,每行两个数字a,b,表示a与b间有一条边再接下来M行,每行三个数字x