ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

线段树合并 & 权值线段树 & 一些题目

2022-05-13 09:34:04  阅读:158  来源: 互联网

标签:题目 val int 线段 maxdata 基站 权值 root


https://www.luogu.com.cn/blog/styx-ferryman/xian-duan-shu-ge-bing-zong-ru-men-dao-fang-qi?tdsourcetag=s_pcqq_aiomsg

%%%

权值线段树的一些操作

int getrank(int x){
    return query_num(root,1,maxdata,1,x)+1;
}
int getpre(int x){//查询前驱
    int tmp=query_num(root,1,maxdata,1,x);
    return query_kth(root,1,maxdata,tmp);   
}
int getnxt(int x){//查询后继
    int tmp=query_num(root,1,maxdata,1,x);
    return query_kth(root,1,maxdata,tmp+1);
}

A雨天的尾巴

树上差分,每次操作都单点修改,操作完之后dfs合并线段树。

每个点都开一个权值线段树,维护出现次数最多的权值和出现次数最多的权值的出现次数。

B bzoj4636蒟蒻的序列

区间修改(但又不是完全的区间修改),如果按照提意来去跑叶子节点每次询问的时间复杂度会到\(O(n\log n)\)。考虑线段树每个节点都维护一个懒标,表示所表示区间内所有数的权值,线段树dfs查询。

update
if(L<=l && r<=R){
   tr[rt].lazytag=max(tr[rt].lazytag,k);
   return;
}
query
w=max(w,tr[rt].lazytag);
if(!rt){
   return 1ll*(R-L+1)*w;
}

C Promotion Counting

这个应该是最裸的板子。树上每一个节点开一个权值线段树维护桶,在树上dfs合并。统计答案的时候每个点都扫一遍,在其对应的线段树上查询比\(val_i\)大的数的个数

所以dfs序+分块完全可以做到

D 永无乡

用并查集维护连通块,每一个连通块内建一个权值线段树维护桶。点之间连边就对应所在连通块线段树的合并,然后查询排名就是板子

E 魔法少女Ljj

没啥难的,就是上面几个板子的综合

但是Chano很郁闷,你能帮帮他吗

F大根堆

考虑树形dp。设\(f[u][val]\)为以u为根节点的树中选出的最大值不超过\(val\)的最多的节点,用线段树来维护。
则有

f[u][val]+=f[v][val]//v为u的子节点,此操作对应线段树合并

f[u][val[u]]=max(f[u][val[u]],f[u][val[u]-1]+1);

而且不仅是\(f[u][val[u]]\)要更新,所有小于\(f[u][val[u]-1]+1\)的\(f[u][val]\)都要更新,由于\(f\)本身的是单调的,所以考虑二分查找并且区间更新

涉及到区间更新所以我打了lazy,但是这样可能会开出很多没有用的点来,整一个回收栈,把合并后没有用的点的编号扔到栈里,开新点的时候取出栈里的编号即可(注意扔的时候清空点的信息)

最后答案为\(f[1][maxdata]\)

void dfs(int u,int fa){
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa)continue;
        dfs(v,u);
        root[u]=merge(root[u],root[v],1,maxdata);
    }
    int tmp=query(root[u],1,maxdata,val[u]-1)+1;
    if(tmp<=query(root[u],1,maxdata,val[u]))return;
    int l=val[u],r=maxdata,ans=0;
    while(l<=r){
        int mid=l+r>>1;
        if(query(root[u],1,maxdata,mid)<tmp){
           ans=mid;l=mid+1;
        }
        else r=mid-1;
    }
    update(root[u],1,maxdata,val[u],ans,1);
}

G 领导集团问题LuoguP4577

和 F 大根堆一样,就是变成了小根堆

H 大融合LuoguP4219

以题面上的图为例,查询<3,8>,先把<3,8>断开,3所在的联通块内有3个点,8所在的联通块内有2个点,从3所在的联通快内任意一个点出发到8所在的联通块内的任意一点,需要经过<3,8>的路径有2*3条,所以查询结果应为6

按照这个思路有一个很显然的做法,从u开始dfs,遇到<u,v>跳过,算出u所在联通块内的点数,v同理。\(40pts\)

离线处理,把所有的边都连上,处理出dfs序来,对于每一个联通块开一个权值线段树来维护当前状态下\(dfn[i]\)出现的次数(有没有出现)。

查询

如果x是y的父亲,则答案为 联通块内\(dfn[y]--outdfn[y]\)出现总次数(y一侧的点数)与所在联通块内的总点数减去y一侧的点数 (x一侧的点数)的乘积,这样就不用dfs以断开<x,y>

更新

并查集维护联通块再线段树合并即可

G 基站选址\(LuoguP2605\)

朴素方程

f[i][j]第j个基站建在i最小费用,cost[i][j]第i~j个村庄之间没有被基站i,j覆盖的村庄所需的赔偿费用
f[i][j]=min(f[k][j-1]+cost[k][i])+c[i] k=[j-1,i) 

当我们推导 \(i\) 时,我们只考虑了它和前面的基站产生的影响,这时对于最后一个基站我们不会考虑它和之后的村庄产生的影响,则我们可以在最后增加一个村庄
,保证它必定被作为基站(无建设费用)且不对前面产生影响,这样就不会有遗漏的了

++n,++k,w[n]=d[n]=Inf;

第二维只与上一次有关,可以滚掉,在最外层枚举j即可

f[i]=min(f[k]+cost[k][i])+c[i] k=[j-1,i) 

难点在于求出 \(cost\)

维护两个数组, \(st_i\) 点 \(i\) 的左边界(建基站能覆盖到\(i\)的最靠左的点), \(ed_i\) 点\(i\) 的右边界,二分查找即可

对于一个点 \(i\) 如果 \(ed_i\) 没有建基站,那么 \(i\) 就要赔偿 赔偿总费用为 \(1--st_i-1\)赔偿的费用加上 \(w_i\) 用线段树区间更新

如果在 \(ed_i\)处建基站,赔偿费用就为\(1--st_i-1\)赔偿的费用 不用更新

状态初值 \(i=1\) 第一个基站建在哪个位置

可能会有多个点的有边界为j,用邻接表维护
int sum=0;//cost[1][j-1];
for(int j=1;j<=n;++j){
   f[j]=sum+c[j];
        计算如果不在j建基站需要赔偿的费用
	for(int p=G.head[j];p;p=G.e[p].next){
            int v=G.e[p].to;sum+=w[v];
	}
}
ans=f[n];

线段树中维护 \(min(f[p]+cost[p][j]),\) \(j\) 为当前决策的点

转移

for(int j=1;j<=n;++j){
   if(j-1>=i-1)
   f[j]=S.query(root,1,n,i-1,j-1)+c[j];
   else f[j]=c[j];
   计算如果不在j建基站需要赔偿的费用
   for(int p=G.head[j];p;p=G.e[p].next){
      int v=G.e[p].to;
      if(st[v]>1){        
         S.update(root,1,n,1,st[v]-1,w[v]);
      }
   }
}

由于把第二维滚掉了,所以在枚举到新的 \(i\) 时要重新建树把原来的状态覆盖掉

for(int i=1;i<=k;++i){
   if(i==1){//初值
     ···
   }
   else{
      S.build(root,1,n);
      ···
      转移
   }
   ans=min(ans,f[n]);
}

标签:题目,val,int,线段,maxdata,基站,权值,root
来源: https://www.cnblogs.com/Chano/p/16265405.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有