【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法

【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法

大家好,又见面了,我是全栈君。

【题目】#58. 【WC2013】糖果公园

【题意】给定n个点的树,m种糖果,每个点有糖果ci。给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j)。q次询问一条链上每个点价值的和或修改一个点的糖果ci。n,m,q<=10^5。

【算法】树分块+带修改莫队算法

【题解】参考:WC 2013 糖果公园 park 题解   by vfleaking

首先树分块,参考王室联邦的方法。确定块大小为B,一遍DFS可以分成若干大小为[B,3B]的块,性质是块内两点距离至多为B。

定义(x,y,t)表示询问经过了t次修改的树链x-y的答案,将询问排序:第一关键字belong[x],第二关键字belong[y],第三关键字t。

对于一个询问,要考虑从上一次询问(x’,y’,t’)转移。首先转移t,只需要记录每次修改前和修改后的数值,就可以实现修改或逆修改了。

然后是从树链x’-y’转移到树链x-y‘,这里需要异或操作(对称差)。所谓异或操作,就是如果x标记过就减去答案并消除标记,如果x没标记过就加上答案并增加标记。

具体过程可以参考vfk的公式推导,感性理解也很简单:定义t(x,y)表示除了lca(x,y)的树链x-y,那么 t(x,y’) = t(x’,y’) ^ t(x,x’)

有了这个,我们只要在当前基础上异或一下t(x,x’)和t(y,y’)就可以实现从x’-y’转移到x-y了,当然LCA全部另外算就可以了。

另外,之前修改点x的数值时,如果在当前答案中必须消除,修改,再加入。

【复杂度分析】假设块大小为B。(随便看看就好了,这部分不保证正确……)

如果u和v都不移出块,那么位置移动复杂度O(q*B),时间移动复杂度O(q)。

如果v移出块,那么因为belong[u]只有n/B种可能,位置移动复杂度O(n*n/B)。

如果u移出块,那么位置移动的复杂度O(n)。

而belong[u],belong[v]只有(n/B)^2种可能,所以时间移动的复杂度是O(q*(n/B)^2)。

平衡后B=n^(2/3)。

所以总复杂度O(n^(5/3))。

因为树分块的块大小实际上比B大,所以取B=N^(2/3)*0.5时常数比较优秀。

有一个常数友好的优化,即使询问时如果x所在块编号比y大,那么交换,这样y的移动就会少一半的空间。至多优化一半的常数。

【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法
【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法

#include<cmath> #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; int read(){ int s=0,t=1;char c; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } const int maxn=100010; int tot,n,m,q,B,top,cnt,c0,c1; int first[maxn],deep[maxn],f[maxn][20],belong[maxn],st[maxn],num[maxn],c[maxn],w[maxn],v[maxn],pre[maxn]; long long ans,ANS[maxn]; bool vis[maxn]; struct edge{ int v,from;}e[maxn*2]; struct C0{ int x,y,pre;}a[maxn]; struct C1{ int x,y,t,id;}b[maxn]; void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} void dfs(int x,int fa){ int lim=top; for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1]; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ deep[e[i].v]=deep[x]+1; f[e[i].v][0]=x; dfs(e[i].v,x); if(top-lim>=B){ cnt++; while(top>lim)belong[st[top--]]=cnt; } } st[++top]=x; } int lca(int x,int y){ if(deep[x]<deep[y])swap(x,y); int d=deep[x]-deep[y]; for(int j=0;(1<<j)<=d;j++)if((1<<j)&d)x=f[x][j]; if(x==y)return x; for(int j=17;j>=0;j--)if((1<<j)<=deep[x]&&f[x][j]!=f[y][j])x=f[x][j],y=f[y][j]; return f[x][0]; } void reverse(int x){ if(vis[x])ans-=1ll*w[num[c[x]]--]*v[c[x]]; else ans+=1ll*w[++num[c[x]]]*v[c[x]]; vis[x]^=1; } void modify(int x,int y){ if(!vis[x])c[x]=y; else reverse(x),c[x]=y,reverse(x); } void solve(int x,int y){ while(x!=y){ if(deep[x]>deep[y])reverse(x),x=f[x][0]; else reverse(y),y=f[y][0]; } } bool cmp(C1 a,C1 b){ return belong[a.x]<belong[b.x]||(belong[a.x]==belong[b.x]&&belong[a.y]<belong[b.y])|| (belong[a.x]==belong[b.x]&&belong[a.y]==belong[b.y]&&a.t<b.t);} int main(){ n=read();m=read();q=read(); for(int i=1;i<=m;i++)v[i]=read(); for(int i=1;i<=n;i++)w[i]=read(); for(int i=1;i<n;i++){ int u=read(),v=read(); insert(u,v);insert(v,u); } B=pow(n,2.0/3)*0.5; dfs(1,0); while(top)belong[st[top--]]=cnt; for(int i=1;i<=n;i++)c[i]=pre[i]=read(); for(int i=1;i<=q;i++){ int kind=read(),x=read(),y=read(); if(!kind){ a[++c0]=(C0){x,y,pre[x]},pre[x]=y; } else{ b[++c1]=(C1){x,y,c0,c1}; if(belong[b[c1].x]>belong[b[c1].y])swap(b[c1].x,b[c1].y); } } sort(b+1,b+c1+1,cmp); for(int i=1;i<=b[1].t;i++)modify(a[i].x,a[i].y); solve(b[1].x,b[1].y); int c=lca(b[1].x,b[1].y); reverse(c);ANS[b[1].id]=ans;reverse(c); for(int i=2;i<=c1;i++){ for(int j=b[i-1].t+1;j<=b[i].t;j++)modify(a[j].x,a[j].y); for(int j=b[i-1].t;j>b[i].t;j--)modify(a[j].x,a[j].pre); solve(b[i-1].x,b[i].x);solve(b[i-1].y,b[i].y); int c=lca(b[i].x,b[i].y); reverse(c);ANS[b[i].id]=ans;reverse(c); } for(int i=1;i<=c1;i++)printf("%lld\n",ANS[i]); return 0; }

View Code

 

这道题主要是树上莫队和带修改莫队的结合:

1.树上莫队没有什么高论,就是把分块换成树分块,然后转移区间的时候使用(x,x’)和(y,y’)而已。

2.待修改莫队一个是关键字排序,另一个是对称差操作。

然后本题之所以必须考虑分块,是因为询问的信息是需要整条链的每个节点的信息,这不得不对链暴力。

转载于:https://www.cnblogs.com/onioncyc/p/8573121.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/107648.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 不就是Java编程嘛,来来来

    点击上方☝Java编程技术乐园,轻松关注!及时获取有趣有料的技术文章做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开! …

  • acwing-2983. 玩具(计算几何)

    acwing-2983. 玩具(计算几何)计算玩具收纳盒中,每个分区内的玩具数量。约翰的父母有一个烦恼—-约翰每次玩完玩具以后总会将玩具乱扔。他们为约翰准备了一个长方形的玩具收纳盒,用来放他的玩具。但是约翰非常调皮,每次都非常随意的将玩具扔进盒子中,使得所有玩具都随意混在一起,这让约翰难以找到他喜欢的玩具。对此,约翰的父母想出了一个对策,用若干个纸板将收纳盒分隔成若干个分区,这样至少扔到不同分区的玩具之间还是能分开的。下面是一个收纳盒的俯视图示例。1.jpg你的任务是,每当约翰将玩具扔进收纳盒中时,确定每个分区中有多少个玩具。输

  • django drf_golang源码分析

    django drf_golang源码分析序列化与反序列化一般后端数据返回给前端的数据格式都是json格式,简单易懂,但是我们使用的语言本身并不是json格式,像我们使用的Python如果直接返回给前端,前端用的javascript语言是识

  • 后台管理系统 – 权限设计

    后台管理系统 – 权限设计一、前言对于前端项目特别是中后台管理系统项目,权限设计是最复杂的点之一。一般来说权限设计需要后端来把关,毕竟相对来说前端是无法保证安全的,前端的代码和数据请求都可以伪造。而前端的权限设计更多是为了用户体验的考虑。前端保证体验,后端保证安全。由于前后端的开发差异和侧重点不同,在权限设计上也不一样。后端更多的是根据功能对象划分不同的权限模块,针对接口相应进行权限判断;而前端更多是针对页面路由进行模块划分,针对页面可访问进行判断。接下来将以后台管理系统为例,分享个人对前端权限设计的见解。(具体内容尽量做

  • 基于Auto.js的萌猫跳辅助

    基于Auto.js的萌猫跳辅助许久不见,甚是想念被学长唤醒的博客魂ing…这次是一个失去时效性的小脚本,但是其中包括一些东西或许对你们可以有帮助撒一些要点因为Auto.js并没有直接的对于触控位置的监听,所以需要对安卓API进行调用2.涉及对于画布的使用importClass(android.graphics.PorterDuffXfermode);importClass(android.graphics.PorterDuff);constBG_COLOR=colors.parseColor(“#2d

  • linux怎么进入图形化界面_linux启动过程详解

    linux怎么进入图形化界面_linux启动过程详解GlassFish社区实现了开源JavaEE5应用服务器。GlassFish是一款强健的商业兼容应用服务器,达到产品级质量,可免费用于开发、部署和重新分发。GlassFish是用于构建JavaEE5应用服务器的开源开发项目的名称。它基于SunMicrosystems提供的SunJavaSystemApplicationServerPE9的源代码以及Or…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号