• BZOJ.4919.[Lydsy1706月赛]大根堆(线段树合并/启发式合并)


    题目链接

    考虑树退化为链的情况,就是求一个最长(严格)上升子序列。
    对于树,不同子树间是互不影响的。仿照序列上的LIS,对每个点x维护一个状态集合,即合并其子节点后的集合,然后用val[x]替换掉第一个大于它的数(有等于的就不换了)。
    最后根节点状态集合的大小就是答案了。
    关于替换数,可以先找到这个数的位置,如果有这个数就不用管了;没有的话插入进去,然后递归回去,找到一个靠右的位置删掉。
    当然其实不用线段树合并这么麻烦,直接上multiset启发式合并就可以了。。

    注意是合并了子树的状态,so叶节点size>1也是可能的!不要直接置为0!(应该-=1)

    线段树合并比set启发式合并慢多了。。。
    DFS可以用for一遍n~1取代。

    另外有另一种线段树合并的方法,要区间修改、标记永久化,这里大体记一下具体步骤:

    以下是口胡,建议忽略。
    (x)从子树转移,就是(f[x][j]=f[v1][j]+f[v2][j]+...),对应位置相加;最后转移完对区间(val_xsim INF)(f[x][val_x-1]+1)(max)
    (CQzhangyu)的代码里(tag)是区间取(max)的标记,(sum)是区间加的标记。(sum)和正常一样在访问节点前下传。(tag)为什么感觉不标记永久化一样下传也可以啊...当然也没必要下传。
    合并的时候,假设现在是把(y)合并到(x)上(是对应位置做加法)。
    如果(lson[x]=0),那说明(lson[x])的最大值就是(x)的最大值(tag[x]),所以之前(x)上的给(lson[x])的区间加标记(sum[x])就不用管了。((x)节点上的最大值是已经被(sum)更新后的)
    所以如果(lson[x]=0, lson[y] eq0),合并是对应位置相加,所以(lson[x])的值应该变成(lson[x]+lson[y])
    而现在(lson[x]=0),而(tag[lson[x]])其实等于(tag[x]),所以对应位置相加后,现在的(tag[lson[x]])就变成(tag[x]+tag[lson[y]])
    (lson[y])的区间加标记可能没下传,要保留,现在加上(lson[x])的值就变成了(tag[x]+sum[lson[y]])
    其它时候同理吧...

    //64956kb	1288ms
    #include <cstdio>
    #include <cctype>
    #include <cassert>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    const int N=2e5+5;
    
    int n,A[N],cnt,ref[N],root[N],Enum,H[N],nxt[N],to[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Segment_Tree
    {
    	#define S N*19
    	#define lson son[x][0]
    	#define rson son[x][1]
    	int tot,sz[S],son[S][2];
    	bool flag;
    
    	#define Update(x) sz[x]=sz[lson]+sz[rson]
    	void Delete(int x,int l,int r)
    	{
    		if(l==r)
    		{
    			flag=0, --sz[x];//not sz[x]=0!
    			return;
    		}
    		if(sz[lson]) Delete(lson,l,l+r>>1);
    		else/*if(sz[rson])*/ Delete(rson,(l+r>>1)+1,r);
    		Update(x);
    	}
    	void Insert(int &x,int l,int r,int p)
    	{
    		if(!x) x=++tot;
    		if(l==r)
    		{
    			if(!sz[x]) flag=1, ++sz[x];
    			return;
    		}
    		int m=l+r>>1;
    		if(p<=m)
    		{
    			Insert(lson,l,m,p);
    			if(flag&&sz[rson]) Delete(rson,m+1,r);//新建的时候肯定删不了啊(没有右子树) 
    		}
    		else Insert(rson,m+1,r,p);
    		Update(x);
    	}
    	int Merge(int x,int y)
    	{
    		if(!x||!y) return x^y;
    		lson=Merge(lson,son[y][0]), rson=Merge(rson,son[y][1]);
    		sz[x]+=sz[y], sz[y]=0; return x;
    	}
    }T;
    
    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 void AddEdge(int u,int v){
    	/*if(u)*/ to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    }
    int Find(int x)
    {
    	int l=1, r=cnt, mid;
    	while(l<r)
    		if(ref[mid=l+r>>1]<x) l=mid+1;
    		else r=mid;
    	return l;
    }
    void DFS(int x)
    {
    	for(int i=H[x]; i; i=nxt[i])
    		DFS(to[i]), root[x]=T.Merge(root[x],root[to[i]]);
    	T.flag=0, T.Insert(root[x],1,cnt,A[x]);
    }
    
    int main()
    {
    	n=read();
    	for(int i=1; i<=n; ++i) ref[i]=A[i]=read(), AddEdge(read(),i);
    
    	std::sort(ref+1,ref+1+n), cnt=1;
    	for(int i=2; i<=n; ++i) if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
    	for(int i=1; i<=n; ++i) A[i]=Find(A[i]);
    
    	DFS(1), printf("%d
    ",T.sz[root[1]]);
    
    	return 0;
    }
    
  • 相关阅读:
    LeetCode——230. 二叉搜索树中第K小的元素
    LeetCode——456.132模式
    LeetCode——623.在二叉树中增加一行
    LeetCode——735.行星碰撞
    翻译——2_Linear Regression and Support Vector Regression
    LeetCode——919.完全二叉树插入器
    论文翻译——Deep contextualized word representations
    LeetCode——853.车队
    Leetcode——863.二叉树中所有距离为 K 的结点
    Pytorch——BERT 预训练模型及文本分类
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9303077.html
Copyright © 2020-2023  润新知