题解 P4556 【[Vani有约会]雨天的尾巴】

_虹_

2019-05-17 19:19:02

Solution

分享一个**不写任何数据结构**就能++过去的方法。 ------------ 原理同线段树合并解法,可以移步其它大佬题解。 这里讲一下怎么不写线段树合并过这道题。 可以发现在线段树合并解法中,我们需要维护的信息其实是一个二元组(**种类,数量**),放到线段树上,就是(**位置,值**),而位置在线段树上固定了下来,所以在子节点维护值,上层阶点维护最大的值和对应的位置。~~然后卡一卡空间就好了。~~ 但是对于(**位置,值**)这个二元组,换用平衡树维护显然也是完全可以的,而且空间复杂度要更优。 ~~写平衡树是不可能的,能不写平衡树就不写平衡树的,只有用stl才能水题的样子,里面的东西都超好用的,我超喜欢stl的。~~ 问题是stl里的平衡树显然没法拓展,那么我们怎么用它维护答案呢。 stl的map可以很方便的维护(**位置,值**)这个二元组,却不支持根据第二维维护答案。 其实我们用线段树的话,是可以查出任意(l,r)的答案的,不过这道题只需要(1,n)的答案。 那有没有什么只能查出(1,n)的答案,代码更简单的写法呢。 假如现在有了所有的二元组(**位置,值**),且没有相同的值,那么只需要按照第二维进行排序。 如果有相同的值,那只要让第二维升序,第二维相同时第一维降序,排序后最大的一个就是答案了。 然后我们需要让这个有序序列带修,所以用set维护。 现在总结一下,就有实现思路了: 1. map维护(位置,值)二元组 1. set维护(值,位置)二元组,即set维护map的翻转 1. 当修改一个值的时候,map中查找到位置对应的二元组,在set中删除翻转后的二元组,修改map的二元组,在set中插入翻转的新二元组。 至于合并,就是标准的启发式合并。 ~~所以这题可以出成noip题。~~ 时间复杂度大概O(nlog^2n)的样子。 ~~吸口氧就过了。~~ 跑的和我吸氧的线段树合并差不多,都在4300ms左右。(均为树剖lca) ## 倍增lca+stl代码:(倍增短一些) ```cpp #include <cstdio> #include <map> #include <vector> #include <set> using namespace std; const int kmaxn=100000+5; const int kmaxlog=20; vector<int> head[kmaxn]; int n,m; int lg[kmaxn]; int fa[kmaxn][kmaxlog]; int dep[kmaxn]; void init() { for(int i=1;i<=n;++i) lg[i]=lg[i-1]+((1<<lg[i-1])==i); } void dfs(int now,int f) { dep[now]=dep[(fa[now][0]=f)]+1; for(int i=1;i<lg[dep[now]];++i) fa[now][i]=fa[fa[now][i-1]][i-1]; vector<int>& e=head[now]; for(int t=0,len=e.size();t<len;++t) if(e[t]!=f)dfs(e[t],now); } inline int lca(int a,int b) { if(dep[a]<dep[b])swap(a,b); while(dep[a]>dep[b]) a=fa[a][lg[dep[a]-dep[b]]-1]; if(a==b)return a; for(int i=lg[dep[a]]-1;i>=0;--i) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i]; return fa[a][0]; } struct unit { int a,b; unit(int _a=0,int _b=0):a(_a),b(_b){} const bool operator<(const unit& u)const{return a==u.a?b>u.b:a<u.a;} }; class data_struct { public: map<int,int> mp; map<int,int>::iterator itr; set<unit> st; inline void add(int pos,int k) { itr=mp.insert(make_pair(pos,0)).first; if(itr->second!=0) st.erase(unit(itr->second,itr->first)); itr->second+=k; if(itr->second!=0) st.insert(unit(itr->second,itr->first)); else mp.erase(itr); } void _merge(map<int,int>& tmp,set<unit>& tst,map<int,int>::iterator t) { if(tmp.size()>mp.size()){ swap(tmp,mp); swap(tst,st); } t=tmp.begin(); while(t!=tmp.end()){ add(t->first,t->second); ++t; } tmp.clear(); tst.clear(); } inline void merge(data_struct& ds){_merge(ds.mp,ds.st,ds.itr);} }trees[kmaxn]; inline void mod(int x,int y,int z) { trees[x].add(z,1); trees[y].add(z,1); int a=lca(x,y); trees[a].add(z,-1); trees[fa[a][0]].add(z,-1); } void solve(int now) { vector<int> &e=head[now]; int t=0,len=e.size(); while(t<len){ if(e[t]!=fa[now][0]){ solve(e[t]); trees[now].merge(trees[e[t]]); } ++t; } if(trees[now].st.empty()) dep[now]=0; else dep[now]=(--trees[now].st.end())->b; } int a,b,c; int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;++i){ scanf("%d%d",&a,&b); head[a].push_back(b); head[b].push_back(a); } init(); dfs(1,0); while(m--){ scanf("%d%d%d",&a,&b,&c); mod(a,b,c); } solve(1); for(int i=0;i<=n;) printf("%d\n",dep[i++]); return 0; } ``` ~~众所周知set和map不是数据结构【大误~~ ## 也分享一个普通的的线段树合并做法。 ```cpp #include <cstdio> #include <vector> using namespace std; enum SONS { ls,rs }; class node; class autoptr { static node* bpos; public: int offset; inline node& operator*(); inline node* operator->(); inline const autoptr& operator=(const autoptr& a) { offset=a.offset; return *this; } inline const autoptr& operator=(const int o) { offset=o; return *this; } inline operator node*()const; inline operator bool()const { return offset; } inline operator int()const { return offset; } }; class node { public: int v; int pos; autoptr son[2]; node() { v=0; son[ls]=son[rs]=0; } inline void clear() { v=0; son[ls]=son[rs]=0; } inline void update() { v=0; if(!(son[ls]||son[rs])) return; if(!son[ls]||!son[rs]) { pos=son[(bool)son[rs]]->pos; v=son[(bool)son[rs]]->v; } else { bool dir=!(son[ls]->v>=son[rs]->v); v=son[dir]->v; pos=son[dir]->pos; } } }; inline node& autoptr::operator*() { return *(bpos+offset); } inline node* autoptr::operator->() { return (bpos+offset); } inline autoptr::operator node*()const { return bpos+offset; } const int kmaxpool=100000*50; int npt; node mpool[kmaxpool]; node* autoptr::bpos=mpool; inline int alloc_node() { return ++npt; } const int kmaxz=100000+5; class segment_tree { static const int L,R; public: autoptr root; autoptr _merge(int l,int r,autoptr a,autoptr b) { if(!a||!b) return a?a:b; if(l==r) { a->v+=b->v; return a; } int mid=(l+r)>>1; a->son[ls]=_merge(l,mid,a->son[ls],b->son[ls]); a->son[rs]=_merge(mid+1,r,a->son[rs],b->son[rs]); a->update(); return a; } segment_tree merge(segment_tree& st) { root=_merge(L,R,root,st.root); return *this; } void _add(int l,int r,int pos,int k,autoptr& p)//add { if(!p) p=alloc_node(); if(l==r) { p->v+=k; p->pos=l; return; } int mid=(l+r)>>1; if(pos<=mid) r=mid; else l=mid+1; _add(l,r,pos,k,p->son[pos>mid]); p->update(); } void add(int pos,int k) { _add(L,R,pos,k,root); } }; const int segment_tree::L=1; const int segment_tree::R=kmaxz; struct edge { int d; edge *nt; }; const int kmaxn=100000+5; vector<int> head[kmaxn]; void add_edge(int s,int d) { head[s].push_back(d); } int top[kmaxn],fa[kmaxn],dep[kmaxn],hs[kmaxn],size[kmaxn]; void dfs1(int now,int f) { fa[now]=f; dep[now]=dep[f]+1; size[now]=1; vector<int> &e=head[now]; int t=0,len=e.size(); int ms=0; while(t<len) { if(e[t]!=f) { dfs1(e[t],now); size[now]+=size[e[t]]; if(size[e[t]]>size[ms]) ms=e[t]; } ++t; } hs[now]=ms; } void dfs2(int now,int tp) { top[now]=tp; if(!hs[now])return; dfs2(hs[now],tp); vector<int> &e=head[now]; int t=0,len=e.size(); while(t<len) { if(e[t]!=hs[now]&&e[t]!=fa[now]) dfs2(e[t],e[t]); ++t; } } inline int lca(int a,int b) { while(top[a]!=top[b]) { if(dep[top[a]]<dep[top[b]]) swap(a,b); a=fa[top[a]]; } return dep[a]<dep[b]?a:b; } segment_tree trees[kmaxn]; inline void mod(int x,int y,int z) { trees[x].add(z,1); trees[y].add(z,1); int a=lca(x,y); trees[a].add(z,-1); trees[fa[a]].add(z,-1); } void solve(int now) { vector<int> &e=head[now]; int t=0,len=e.size(); while(t<len) { if(e[t]!=fa[now]) { solve(e[t]); trees[now].merge(trees[e[t]]); } ++t; } if(trees[now].root&&trees[now].root->v>0) hs[now]=trees[now].root->pos; else hs[now]=0; } int n,m; int a,b,c; int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;++i) { scanf("%d%d",&a,&b); add_edge(a,b); add_edge(b,a); } dfs1(1,0); dfs2(1,1); ++m; while(--m) { scanf("%d%d%d",&a,&b,&c); mod(a,b,c); } solve(1); for(int i=0;i<=n;) { printf("%d\n",hs[i++]); } return 0; } ``` 线段树指针被卡内存了所以写了个autoptr ~~然而哪里auto了~~