博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[CTSC2008]网络管理Network
阅读量:5931 次
发布时间:2019-06-19

本文共 4892 字,大约阅读时间需要 16 分钟。

Descroption

带修改求树上路径第k大

Solution

方法一:树状数组套主席树

出题人总是比较喜欢把序列的东西搬到树上去

但这并不影响我们做题,因为我们可以用一个log的时间将它变回序列的操作(树链剖分)

使用树状数组,是因为它可以支持单点加和区间求和的操作。

我们求出dfs序,然后按照顺序,将每个点离散后的权值,加到树状数组上去

其实是加到树状数组每个点所对应的权值线段树上

这样,我们处理操作的时候:

  • 修改操作:就和之前的加点一样,在原权值处-1,新权值处+1
  • 询问操作:找出x和y到它们的lca上的log条重链,每条链都对应dfs序的一个区间,我们先记录下这log个区间,然后在权值线段树上二分,对这些区间的端点加加减减一下,就能求出相应权值区间的数量啦

复杂度是\(O(n \log^3 n)\)

之前求成第k小了,wa了无数次,我真菜

Code 

#include
using namespace std;#define ll long longinline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f;}#define reg register#define MN 80005int n,m,val[MN],s[MN<<1],tot;struct OPT{int l,r,k;}o[MN];int ls[MN*200],rs[MN*200],t[MN*200],sz,rt[MN];#define mid ((l+r)>>1)void Modify(int &rt,int l,int r,int x,int v){ ++sz;ls[sz]=ls[rt];rs[sz]=rs[rt];t[sz]=t[rt]+v;rt=sz; if(l==r) return; if(x<=mid) Modify(ls[rt],l,mid,x,v); else Modify(rs[rt],mid+1,r,x,v);}int tmp[400],cnt,sign[400];int Query(int l,int r,int k){ if(l==r) return l; register int sum=0,i; for(i=1;i<=cnt;++i) sum+=t[rs[tmp[i]]]*sign[i]; if(k<=sum) { for(i=1;i<=cnt;++i) tmp[i]=rs[tmp[i]]; return Query(mid+1,r,k); } else { for(i=1;i<=cnt;++i) tmp[i]=ls[tmp[i]]; return Query(l,mid,k-sum); }}#define lb(x) (x&(-x))inline void C(int x,int p,int v){for(;x<=n;x+=lb(x)) Modify(rt[x],1,tot,p,v);}struct edge{int to,nex;}e[MN<<1];int hr[MN],en;inline void ins(int f,int t){ e[++en]=(edge){t,hr[f]};hr[f]=en; e[++en]=(edge){f,hr[t]};hr[t]=en;}int dfn[MN],fdfn[MN],mx[MN],top[MN],siz[MN],fa[MN],dep[MN],dind;void dfs1(int x,int f){ dep[x]=dep[fa[x]=f]+1;siz[x]=1; for(reg int i=hr[x];i;i=e[i].nex) if(f^e[i].to) dfs1(e[i].to,x),siz[e[i].to]>siz[mx[x]]?mx[x]=e[i].to:0,siz[x]+=siz[e[i].to];}void dfs2(int x,int tp){ dfn[x]=++dind;fdfn[dind]=x;top[x]=tp;if(mx[x]) dfs2(mx[x],tp); for(reg int i=hr[x];i;i=e[i].nex) if((fa[x]^e[i].to)&&(mx[x]^e[i].to)) dfs2(e[i].to,e[i].to);}inline void Insert(int l,int r){ for(;r;r-=lb(r)) tmp[++cnt]=rt[r],sign[cnt]=1; for(--l;l;l-=lb(l)) tmp[++cnt]=rt[l],sign[cnt]=-1;}inline void que(int x,int y,int k){ cnt=0;int num=1+dep[x]+dep[y]; while(top[x]^top[y]) { if(dep[top[x]]>dep[top[y]]) Insert(dfn[top[x]],dfn[x]),x=fa[top[x]]; else Insert(dfn[top[y]],dfn[y]),y=fa[top[y]]; } if(dep[x]

方法二:整体二分

仍然是树链剖分

用上整体二分的套路

修改操作可以看成一次加点和一次删点

upd:大概在一周后,终于补上了整体二分的代码......

复杂度仍然是\(O(n \log^3 n)\)

我怎么再次打成了第k小,我是有智力问题吧(疯了)......

Code 

#include
#define ll long long#define max(a,b) ((a)>(b)?(a):(b))#define min(a,b) ((a)<(b)?(a):(b))inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f;}#define MN 30002#define MM 80002int n,tot,ID,val[MM],Ans[MN];int num[MN+MM],cnt;struct opt{int x,y,k,id;}q[MM+(MN<<1)],p[MM+(MN<<1)];struct edge{int to,nex;}e[MM<<1];int hr[MM],en;inline void ins(int f,int t){ e[++en]=(edge){t,hr[f]};hr[f]=en; e[++en]=(edge){f,hr[t]};hr[t]=en;}int dep[MM],fa[MM],top[MM],mx[MM],sz[MM],dfn[MM],dind;inline void dfs1(int x=1,int f=0){ dep[x]=dep[fa[x]=f]+(sz[x]=1);register int i; for(i=hr[x];i;i=e[i].nex)if(e[i].to^f) dfs1(e[i].to,x),(sz[e[i].to]>sz[mx[x]])?mx[x]=e[i].to:0,sz[x]+=sz[e[i].to];}inline void dfs2(int x=1,int tp=1){ dfn[x]=++dind;top[x]=tp;if(mx[x]) dfs2(mx[x],tp);register int i; for(i=hr[x];i;i=e[i].nex)if((e[i].to^fa[x])&&(e[i].to^mx[x])) dfs2(e[i].to,e[i].to);}#define lb(x) (x&(-x))int t[MM];inline void C(int x,int v){for(;x<=n;x+=lb(x))t[x]+=v;}inline int G(int x){int r=0;for(;x;x-=lb(x))r+=t[x];return r;}inline int get_lca(int x,int y){ for(;top[x]^top[y];)if(dep[top[x]]>dep[top[y]])x=fa[top[x]];else y=fa[top[y]]; return dep[x]>dep[y]?y:x;}inline int get_num(int x,int y){ int r=0; for(;top[x]^top[y];) dep[top[x]]>dep[top[y]]?(r+=G(dfn[x])-G(dfn[top[x]]-1),x=fa[top[x]]):(r+=G(dfn[y])-G(dfn[top[y]]-1),y=fa[top[y]]); r+=G(max(dfn[x],dfn[y]))-G(min(dfn[x],dfn[y])-1); return r;}void solve(int l=1,int r=cnt,int ql=1,int qr=tot){ if(ql>qr) return; register int i; if(l==r) { for(i=ql;i<=qr;++i) if(q[i].id) Ans[q[i].id]=num[l]; return; } register int mid=(l+r+1)>>1,pl=ql,pr=qr,tmp; for(i=ql;i<=qr;++i) { if(!q[i].id) { if(q[i].y>=num[mid]) p[pr--]=q[i],C(dfn[q[i].x],q[i].k); else p[pl++]=q[i]; } else { tmp=get_num(q[i].x,q[i].y); if(tmp>=q[i].k) p[pr--]=q[i]; else q[i].k-=tmp,p[pl++]=q[i]; } } for(i=ql;i<=qr;++i) if(!q[i].id&&q[i].y>=num[mid]) C(dfn[q[i].x],-q[i].k); for(i=ql;i


Blog来自PaperCloud,未经允许,请勿转载,TKS!

转载于:https://www.cnblogs.com/PaperCloud/p/10152249.html

你可能感兴趣的文章
为什么开源中国APP4.0版的排版让我感觉有些乱
查看>>
Traffic Analysis of an SSL/TLS Session
查看>>
Debian 7.0 amd64安装ia32-libs
查看>>
ie浏览器被锁定成2345 1125.cc无法修改的解决办法
查看>>
递归查询,只能查询两级
查看>>
Qt网络资源汇总(官网、源码、社区、博客)
查看>>
jQuery的技巧01
查看>>
我的友情链接
查看>>
冻结拆分table
查看>>
微信小程序把玩(十七)input组件
查看>>
12次课(usermod命令、 用户密码管理、mkpasswd命令)
查看>>
Linux中常见的加密技术介绍
查看>>
关于继承的一些小易混淆概念
查看>>
cacti 1分钟采集
查看>>
关于touch触摸屏的实现原理和linux实现
查看>>
云主机管理系统-系统预览
查看>>
目录文件管理工具
查看>>
读程序员的修炼之道
查看>>
基础总结篇之九:Intent应用详解
查看>>
Spring5 异步事件
查看>>