动态淀粉质(划掉)题单&简要题解
简介
动态点分治的思想:还不太清楚诶怎么办。
大概是通过降低树高来降低每次修改和询问的复杂度吧,还可以把树上一个连通块的信息统计到一个点(重心)上。具体实现方式和普通的静态点分治没有太大的区别,只是把点分治时递归到的每层重心用边连起来(当然不是在原树中直接连),构成一个叫做点分树(VPT)的东西,它其实就是个弟弟递归结构。
修改原树信息时(注意这里的修改一般是围绕一个结点进行的,但不一定是单点修改),可以在点分树上找到对应的结点,然后一路爬点分树的树边修改沿路结点的信息,询问的时候和修改差不多。
但是直接这样做会有些问题,容易发现,在点分树上的一对父子结点的担当区域是有交集的(其实就是父结点的担当区域包含子结点的),所以,为了去除重复部分对每次询问答案的影响,通常需要在每个结点处再维护一个这个结点担当区域对其点分树上父结点的贡献,爬树边时把它减去就行了。
看几道例题:
[BZOJ3730]震波
分析
点分树每个结点处维护两棵线段树,一棵存这个结点担当区域的结点对这个结点的贡献,另一个存这个结点担当区域对其父结点的贡献。
这份代码会TLE。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define rin(i,a,b) for(register int i=(a);i<=(b);i++)
#define rec(i,a,b) for(register int i=(a);i>=(b);i--)
#define trav(i,a) for(register int i=head[(a)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;
char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
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;
}
const int MAXN=100005;
int n,m,ecnt,cog,head[MAXN],a[MAXN];
int id[MAXN],num[MAXN],pos[MAXN],dep[MAXN],st[25][MAXN<<1],tot,len;
int fa[MAXN],siz[MAXN],totsiz;
int root1[MAXN],root2[MAXN],lc[MAXN*200],rc[MAXN*200],sum[MAXN*200],loc,ql,qr,kk,tot2;
bool vis[MAXN];
struct Edge{
int to,nxt;
}e[MAXN<<1];
inline void add_edge(int bg,int ed){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
}
void dfs(int x,int pre,int depth){
dep[x]=depth;
id[x]=++tot;
num[tot]=x;
st[0][++len]=tot;
pos[x]=len;
trav(i,x){
int ver=e[i].to;
if(ver==pre) continue;
dfs(ver,x,depth+1);
st[0][++len]=id[x];
}
}
inline void buildst(){
int lim=log2(len);
rin(i,1,lim) rin(j,1,len-(1<<i)+1)
st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
inline int lca(int x,int y){
int xx=pos[x],yy=pos[y];
if(xx>yy) std::swap(xx,yy);
int lim=log2(yy-xx+1);
return num[std::min(st[lim][xx],st[lim][yy-(1<<lim)+1])];
}
inline int getdis(int x,int y){
return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}
void getcog(int x,int pre){
siz[x]=1;
bool flag=1;
trav(i,x){
int ver=e[i].to;
if(ver==pre||vis[ver]) continue;
getcog(ver,x);
siz[x]+=siz[ver];
if(siz[ver]>totsiz/2) flag=0;
}
if(totsiz-siz[x]>totsiz/2) flag=0;
if(flag) cog=x;
}
#define mid ((l+r)>>1)
int upd(int pre,int l,int r){
int o=pre;
if(!o) o=++tot2;
sum[o]+=kk;
if(l==r) return o;
if(loc<=mid) lc[o]=upd(lc[pre],l,mid);
else rc[o]=upd(rc[pre],mid+1,r);
return o;
}
int query(int o,int l,int r){
if(!o) return 0;
if(ql<=l&&r<=qr) return sum[o];
int ret=0;
if(mid>=ql) ret+=query(lc[o],l,mid);
if(mid<qr) ret+=query(rc[o],mid+1,r);
return ret;
}
#undef mid
void buildsgt(int x,int pre,int now){
loc=getdis(x,now)+1,kk=a[x];
root1[now]=upd(root1[now],1,n);
if(fa[now]){
loc=getdis(x,fa[now])+1,kk=a[x];
root2[now]=upd(root2[now],1,n);
}
siz[x]=1;
trav(i,x){
int ver=e[i].to;
if(ver==pre||vis[ver]) continue;
buildsgt(ver,x,now);
siz[x]+=siz[ver];
}
}
void buildvpt(int x){
vis[x]=1;
buildsgt(x,0,x);
trav(i,x){
int ver=e[i].to;
if(vis[ver]) continue;
totsiz=siz[ver];
getcog(ver,x);
fa[cog]=x;
buildvpt(cog);
}
}
inline int getans(int x,int y){
int now=x,ret=0;
while(1){
int curdis=getdis(x,now);
if(curdis<=y){
ql=1,qr=y-curdis+1;
ret+=query(root1[now],1,n);
}
if(!fa[now]) return ret;
curdis=getdis(x,fa[now]);
if(curdis<=y){
ql=1,qr=y-curdis+1;
ret-=query(root2[now],1,n);
}
now=fa[now];
}
}
inline void modi(int x,int y){
int now=x;
while(1){
loc=getdis(x,now)+1,kk=y-a[x];
root1[now]=upd(root1[now],1,n);
if(!fa[now]){a[x]=y;return;}
loc=getdis(x,fa[now])+1,kk=y-a[x];
root2[now]=upd(root2[now],1,n);
now=fa[now];
}
}
int main(){
n=read(),m=read();
rin(i,1,n) a[i]=read();
rin(i,2,n){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
dfs(1,0,1);
buildst();
totsiz=n;
getcog(1,0);
buildvpt(cog);
int lastans=0;
while(m--){
int opt=read();
if(opt==0){
int x=(read()^lastans),y=(read()^lastans);
lastans=getans(x,y);
printf("%d\n",lastans);
}
else{
int x=(read()^lastans),y=(read()^lastans);
modi(x,y);
}
}
return 0;
}
[BZOJ4372]烁烁的游戏
分析
怕和上一道题是一对。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,x) for(int i=head[(x)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;
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;
}
const int MAXN=100005;
int n,m,ecnt,head[MAXN];
int dep[MAXN],id[MAXN],num[MAXN],pos[MAXN],st[25][MAXN<<1],tot,len;
int root,cog,totsiz,fa[MAXN],siz[MAXN];
int root1[MAXN],root2[MAXN],lc[MAXN*200],rc[MAXN*200],sum[MAXN*200],tag[MAXN*200];
int loc,ql,qr,kk,tot2;
bool vis[MAXN];
struct Edge{
int to,nxt;
}e[MAXN<<1];
inline void add_edge(int bg,int ed){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
}
void dfs(int x,int pre,int depth){
dep[x]=depth;
id[x]=++tot;
num[tot]=x;
st[0][++len]=id[x];
pos[x]=len;
trav(i,x){
int ver=e[i].to;
if(ver==pre) continue;
dfs(ver,x,depth+1);
st[0][++len]=id[x];
}
}
inline void buildst(){
int lim=log2(len);
rin(i,1,lim) rin(j,1,len-(1<<i)+1)
st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
inline int lca(int x,int y){
int xx=pos[x],yy=pos[y];
if(xx>yy) std::swap(xx,yy);
int lim=log2(yy-xx+1);
return num[std::min(st[lim][xx],st[lim][yy-(1<<lim)+1])];
}
inline int getdis(int x,int y){
return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}
void getcog(int x,int pre){
siz[x]=1;
bool flag=1;
trav(i,x){
int ver=e[i].to;
if(ver==pre||vis[ver]) continue;
getcog(ver,x);
siz[x]+=siz[ver];
if(siz[ver]>totsiz/2) flag=0;
}
if(totsiz-siz[x]>totsiz/2) flag=0;
if(flag) cog=x;
}
void getsiz(int x,int pre){
siz[x]=1;
trav(i,x){
int ver=e[i].to;
if(ver==pre||vis[ver]) continue;
getsiz(ver,x);
siz[x]+=siz[ver];
}
}
void buildvpt(int x){
vis[x]=1;
getsiz(x,0);
trav(i,x){
int ver=e[i].to;
if(vis[ver]) continue;
totsiz=siz[ver];
getcog(ver,x);
fa[cog]=x;
buildvpt(cog);
}
}
#define mid ((l+r)>>1)
int upd(int pre,int l,int r){
int o=pre;
if(!o) o=++tot;
sum[o]+=(std::min(qr,r)-std::max(ql,l)+1)*kk;
if(ql<=l&&r<=qr){
tag[o]+=kk;
return o;
}
if(mid>=ql) lc[o]=upd(lc[pre],l,mid);
if(mid<qr) rc[o]=upd(rc[pre],mid+1,r);
return o;
}
int query(int o,int l,int r){
if(l==r) return sum[o];
if(loc<=mid) return query(lc[o],l,mid)+tag[o];
else return query(rc[o],mid+1,r)+tag[o];
}
#undef mid
inline int getans(int x){
int now=x,ret=0;
while(1){
loc=getdis(x,now)+1;
ret+=query(root1[now],1,n);
if(!fa[now]) return ret;
loc=getdis(x,fa[now])+1;
ret-=query(root2[now],1,n);
now=fa[now];
}
}
inline void modi(int x,int y,int z){
int now=x;
while(1){
int curdis=getdis(x,now);
if(curdis<=y){
ql=1,qr=y-curdis+1,kk=z;
root1[now]=upd(root1[now],1,n);
}
if(!fa[now]) return;
curdis=getdis(x,fa[now]);
if(curdis<=y){
ql=1,qr=y-curdis+1,kk=z;
root2[now]=upd(root2[now],1,n);
}
now=fa[now];
}
}
int main(){
n=read(),m=read();
rin(i,2,n){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
dfs(1,0,1);
buildst();
totsiz=n;
getcog(1,0);
root=cog;
buildvpt(cog);
while(m--){
char opt=getchar();
while(!isalpha(opt)) opt=getchar();
if(opt=='Q'){
int x=read();
printf("%d\n",getans(x));
}
else{
int x=read(),y=read(),z=read();
modi(x,y,z);
}
}
return 0;
}
[BZOJ3924][ZJOI2015]幻想乡战略游戏
分析
对点分树的每个结点维护\(3\)个值,\(sum1[x],sum2[x],sum3[x]\),分别表示\(x\)担当区域对\(x\)的贡献,对\(fa[x]\)的贡献,以及权值和。
我们可以从点分树的根结点出发,根据当前结点的答案和其点分树上子结点的答案,来推出答案在当前结点还是在其在点分树上的某个子结点的担当区域内,不断递归下去即可得到答案。(这里结点\(x\)的答案指的是\(\sum val[y] \times dist(x,y)\))。
如何求某个结点的答案?还是爬点分树就可以了,在每一层:
\]
代码
BZOJ Rank Last代码,欢迎各位dalao嘲讽。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <vector>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;
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;
}
const int MAXN=100005;
int n,q,ecnt,head[MAXN];
int id[MAXN],num[MAXN],pos[MAXN],st[25][MAXN<<1],tot,len;
int root,cog,totsiz,siz[MAXN],fa[MAXN];
LL dep[MAXN],sum1[MAXN],sum2[MAXN],sum3[MAXN];
bool vis[MAXN];
std::vector<int> vec[MAXN],vec2[MAXN];
struct Edge{
int to,nxt;
LL w;
}e[MAXN<<1];
inline void add_edge(int bg,int ed,LL val){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
e[ecnt].w=val;
head[bg]=ecnt;
}
void dfs(int x,int pre,LL depth){
dep[x]=depth;
id[x]=++tot;
num[tot]=x;
st[0][++len]=id[x];
pos[x]=len;
trav(i,x){
int ver=e[i].to;
if(ver==pre) continue;
dfs(ver,x,depth+e[i].w);
st[0][++len]=id[x];
}
}
inline void buildst(){
int lim=log2(len);
rin(i,1,lim) rin(j,1,len-(1<<i)+1)
st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
inline int lca(int x,int y){
int xx=pos[x],yy=pos[y];
if(xx>yy) std::swap(xx,yy);
int lim=log2(yy-xx+1);
return num[std::min(st[lim][xx],st[lim][yy-(1<<lim)+1])];
}
inline LL getdis(int x,int y){
return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}
void getcog(int x,int pre){
siz[x]=1;
bool flag=1;
trav(i,x){
int ver=e[i].to;
if(ver==pre||vis[ver]) continue;
getcog(ver,x);
siz[x]+=siz[ver];
if(siz[ver]>totsiz/2) flag=0;
}
if(totsiz-siz[x]>totsiz/2) flag=0;
if(flag) cog=x;
}
void getsiz(int x,int pre){
siz[x]=1;
trav(i,x){
int ver=e[i].to;
if(ver==pre||vis[ver]) continue;
getsiz(ver,x);
siz[x]+=siz[ver];
}
}
void buildvpt(int x){
vis[x]=1;
getsiz(x,0);
trav(i,x){
int ver=e[i].to;
if(vis[ver]) continue;
totsiz=siz[ver];
getcog(ver,x);
fa[cog]=x;
vec[x].push_back(cog);
vec2[x].push_back(ver);
buildvpt(cog);
}
}
inline void upd(int x,LL y){
int now=x;
while(1){
sum1[now]+=y*getdis(x,now);
sum3[now]+=y;
if(!fa[now]) return;
sum2[now]+=y*getdis(x,fa[now]);
now=fa[now];
}
}
inline LL getv(int x){
int now=x;LL ret=0;
while(1){
ret+=sum1[now];
if(!fa[now]) return ret;
ret+=(sum3[fa[now]]-sum3[now])*getdis(x,fa[now]);
ret-=sum2[now];
now=fa[now];
}
}
int query(int x){
LL now=getv(x);
rin(i,0,(int)vec[x].size()-1){
int ver=vec2[x][i];
if(getv(ver)<now) return query(vec[x][i]);
}
return x;
}
int main(){
n=read(),q=read();
rin(i,2,n){
int u=read(),v=read();LL w=read();
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs(1,0,0);
buildst();
totsiz=n;
getcog(1,0);
root=cog;
buildvpt(cog);
while(q--){
int x=read();LL y=read();
upd(x,y);
printf("%lld\n",getv(query(root)));
}
return 0;
}
[BZOJ1095][ZJOI2007]Hide 捉迷藏
还没做。
动态淀粉质(划掉)题单&简要题解的更多相关文章
- 虚树总结&题单&简要题解
简介 虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的LCA),建一棵新树,这棵新树就是虚树.通过虚树,可以有效的减小询问(甚至修改)的复杂度.设询问点的个数是\(k\),那么建虚 ...
- $FFT/NTT/FWT$题单&简要题解
打算写一个多项式总结. 虽然自己菜得太真实了. 好像四级标题太小了,下次写博客的时候再考虑一下. 模板 \(FFT\)模板 #include <iostream> #include < ...
- loj2497 [PA2017]Banany(动态淀粉质)
link 给定一棵树,点有点权,边有边权,你每次修改一个点点权或者是修改一个边边权 你一开始在1号点,你每次改节点之后你需要移动到另一个节点,满足这个节点权值减去路径长度最大(下一次从这个节点移动)如 ...
- Codeforces 863 简要题解
文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...
- [小结] 中山纪念中学2018暑期训练小结(划掉)(颓废记)-Day10
[小结] 中山纪念中学2018暑期训练小结(划掉)(颓废记)-Day10 各位看众朋友们,你们好,今天是2018年08月14日,星期二,农历七月初四,欢迎阅看今天的颓废联编节目 最近发生的灵异事件有 ...
- Word中一条删除不掉的单或双横线的解决办法
Word中一条删除不掉的单或双横线 有时你或许会遇到这样一种情况:在word中,有一条单或双横线怎么都删除不了,并且具有这样的特点: 在上面输入文字,横线会自动下调一行,如果文章过页,每页的尾部会有一 ...
- LCT总结——应用篇(附题单)(LCT)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--概念篇戳这里 题单 灰常感谢XZY巨佬提供的强力资磁!(可参考XZY巨佬的博客总结) 题单对于系 ...
- 【转载】LCT题单
本篇博客的题单转载自FlashHu大佬的博客:LCT总结--应用篇(附题单)(LCT). 关于\(LCT\)可以查看这篇博客:\(LCT\)入门. 这里面有些题解的链接是空链接,尚未补全. 维护链信息 ...
- 点分治题单(来自XZY)
点分治题单(来自XZY) 静态点分治 [x] 洛谷 P3806 [模板]点分治1 [x] 洛谷 P4178 Tree [x] 洛谷 P2634 [国家集训队]聪聪可可 [x] 洛谷 P4149 [IO ...
随机推荐
- kettle入门大数据管理工具
研究 kettle 的使用 大佬博客:https://www.cnblogs.com/mq0036/p/9238646.html 国内镜像下载:http://mirror.bit.edu.cn/pen ...
- Go语言入门篇-使用Beego构建完整web应用
使用Beego构建完整web应用 一.GO简介(Beego应用go编写) 1.为什么用GO (1).语法简单 (2).简洁的并发 (3).开发和执行效率快(编译型语言) 2.GO语言环境 下载go & ...
- Pyinstaller-封装python
1. 当程序中没有调用matplotlib模块 ① pip intall pyinstaller ② 在cmd环境下,pyinstaller -F xxx.py 2.当程序中调用matplotlib ...
- Canvas入门07- 自定义实现虚线的绘制
预备知识 直线的斜率 一条直线与某平面直角坐标系x轴正半轴方向的夹角的正切值即该直线相对于该坐标系的斜率. 对于一条直线 y = kx +b,k就是直线的斜率. 斜率的计算 对于一条已知的线段,求斜率 ...
- python 开启进程两种方法 multiprocessing模块 介绍
一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu\_count\(\)查看),在python中大部分情况需要使用多进 ...
- 1000行基本SQL
/* Windows服务 */ -- 启动MySQL net start mysql -- 创建Windows服务 sc create mysql binPath= mysqld_bin_path(注 ...
- Apache 强制SSL访问
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R ...
- Luogu P5339 [TJOI2019]唱、跳、rap和篮球
题目 设\(f_i\)表示从\((a-4i,b-4i,c-4i,d-4i)\)中选\(n-4i\)个排队的方案数. 那么我们可以容斥,答案为\(\sum\limits_{i=0}^{lim}(-1)^ ...
- 小白学Python——Matplotlib 学习(3) 函数图形
import matplotlib.pyplot as plt import numpy as np x = np.linspace(-1,1,50) y = 2*x + 1 plt.figure() ...
- 探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs
原文:探索ASP.Net Core 3.0系列二:聊聊ASP.Net Core 3.0 中的Startup.cs 前言:.NET Core 3.0 SDK包含比以前版本更多的现成模板. 在本文中,我将 ...