zoukankan      html  css  js  c++  java
  • BZOJ.3307.雨天的尾巴(dsu on tree/线段树合并)

    BZOJ
    洛谷

    (dsu on tree)(线段树合并的做法也挺显然不写了)
    如果没写过(dsu)可以看这里

    对修改操作做一下差分放到对应点上,就成了求每个点子树内出现次数最多的颜色,这就和CF600E类似了。直接用(dsu)
    修改某个颜色出现次数的时候,最大值不能(O(1))求出,得套个(set),所以复杂度是(O(qlog^2n))。但常数并不大。

    关于复杂度,在CF600E中对一个点的修改是(O(1))的,而本题中可能是(O(q))(一个点上挂着很多次修改)。但(dsu)的复杂度保证在于,每个点只会被统计(O(log n))次,所以不算(set)复杂度依旧是(O(qlog 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
    ",Ans[i]);
    
    	return 0;
    }
    
  • 相关阅读:
    HTML5 拖放(Drag 和 Drop)详解与实例
    JS中的六大数据类型
    关于创建本地docker仓库
    关于使用国内dock仓库,网易、DaoCloud
    关于Docker开通远程访问端口2375
    多个消费者监听同一个队列
    SQLite -附加数据库
    解密JDK8 枚举
    LoraLU
    深入理解display属性
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10231703.html
Copyright © 2011-2022 走看看