bzoj3730. 震波

动态点分治

考虑从每一个"块"里找到距离k范围内的点的和

为了去重,

每个x维护两个线段树:(都是关于自己分治树子树的点)

1.下标为距离x的距离,权值为val的

2.下标为距离x的分治树father的距离,权值为val

 

这样,统计的时候

计算分治树祖先块的时候,把从自己那里出来的块的东西再减去

 

注意:

1.点分治nowrt是全局变量,所以divi这一层用tmp额外记录一下,否则回溯回来再下去的时候,nowrt就不是这一层的了

2.不能某个祖先的dis大于k,就直接break了。因为到分治树祖先的距离没有单调性!!!!

 

代码:

#include
#define il inline
#define reg register int
#define numb (ch^'0')
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
il void rd(int &x){char ch;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=100000+5;
int n,m;
struct node{int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){e[++cnt].nxt=hd[x];e[cnt].to=y;hd[x]=cnt;
}
int fa[N];
int sz[N];
int mx[N];
int nowsz;
int tmprt;
int rt[N][2];//0 is myself ; 1 is disfather
int nowrt;
struct tr{int ls,rs;int sum;
}t[N*50];
int tot;
int dist[N][20];
int dep[N];
int val[N];
int vis[N];
void pushup(int x){t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum;
}
void upda(int &x,int l,int r,int p,int v){if(!x) x=++tot;if(l==r){t[x].sum+=v;return;}if(p<=mid) upda(t[x].ls,l,mid,p,v);else upda(t[x].rs,mid+1,r,p,v);pushup(x);
}
int query(int x,int l,int r,int L,int R){//no dis=0 (myself)if(L>R) return 0;if(L<=l&&r<=R){return t[x].sum;}int ret=0;if(L<=mid) ret+=query(t[x].ls,l,mid,L,R);if(mid1,r,L,R);return ret;
}
void dfs1(int x,int f,int d,int dis){//find nowrtsz[x]=1;mx[x]=0;dep[x]=d;if(d!=1){upda(tmprt,0,n,dis,val[x]);}for(reg i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(vis[y]||y==f) continue;dfs1(y,x,d,dis+1);sz[x]+=sz[y];mx[x]=max(mx[x],sz[y]);}mx[x]=max(mx[x],nowsz-sz[x]);if(mx[x]<=nowsz/2&&(nowsz-sz[x])<=nowsz/2){nowrt=x;}
}
void dfs2(int x,int f,int d,int dis){//find dis from nowrtsz[x]=1;dep[x]=d;dist[x][d]=dis;upda(rt[nowrt][0],0,n,dis,val[x]);for(reg i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(vis[y]||y==f) continue;dfs2(y,x,d,dis+1);sz[x]+=sz[y];}
}
void divi(int x,int d,int f){//mxsz=0;
//    cout<<" x "<nowrt=0;tmprt=0;dfs1(x,0,d,1);fa[nowrt]=f;rt[nowrt][1]=tmprt;
//    cout<<" nowrt "<dfs2(nowrt,0,d,0);vis[nowrt]=1;int tmp=nowrt;for(reg i=hd[tmp];i;i=e[i].nxt){int y=e[i].to;if(!vis[y]){nowsz=sz[y];divi(y,d+1,tmp);}}
}
int wrk0(int x,int k){int ret=0;ret+=query(rt[x][0],0,n,0,k);int tmp=x;while(fa[x]){int y=fa[x];//    if(k-dist[tmp][dep[y]]>=0) ret+=query(rt[y][0],0,n,0,k-dist[tmp][dep[y]]);//    else break;ret-=query(rt[x][1],0,n,0,k-dist[tmp][dep[y]]);x=y;}return ret;
}
void wrk1(int x,int v){int tmp=x;int c=v-val[x];val[x]=v;upda(rt[x][0],0,n,0,c);while(fa[x]){int y=fa[x];upda(rt[x][1],0,n,dist[tmp][dep[y]],c);upda(rt[y][0],0,n,dist[tmp][dep[y]],c);x=y;}
}
int main(){rd(n);rd(m);for(reg i=1;i<=n;++i) rd(val[i]);int x,y;for(reg i=1;ii){rd(x);rd(y);add(x,y);add(y,x);}nowsz=n;divi(1,1,0);
//    cout<<" toot "<int op,k;int lasans=0;while(m--){rd(op);rd(x);rd(k);x^=lasans;k^=lasans;if(op==0) printf("%d\n",lasans=wrk0(x,k));else wrk1(x,k);}return 0;
}}
signed main(){
//    freopen("data.in","r",stdin);
//    freopen("my.out","w",stdout);
    Miracle::main();return 0;
}/*Author: *Miracle*Date: 2019/2/6 10:11:29
*/

总结:

动态点分治往往为了去重,经常考虑x对fa的贡献

相当于在分治树的往fa走的边上造了一个线段树

(每个点一个,每个边一个,一共n+n-1个)

转载于:https://www.cnblogs.com/Miracevin/p/10353703.html


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部