洛谷P3345:[ZJOI2015]幻想乡战略游戏 (动态点分治)
2018-03-20 19:21
459 查看
题目传送门:https://www.luogu.org/problemnew/show/P3345
题目分析:这题简直是噩梦,调了我一个下午。
首先考虑暴力怎么做:在树上随机找一个点,然后计算以该点为补给点的答案,并算出它周围的点的答案。如果它周围存在一个点答案更优,就往该点走,直到当前点为最优点就停止。这样一次询问就是n2dn2d的,其中d=20d=20,因为每个点的度数不超过20。
上面这个做法的正确性,可以用一个看上去显然的结论证明:如果当前最优点为root,那么对于两个点u,v,若v在u到root的路径上,则v处算得的答案比u处优。这个结论的证明自己YY一下即可反正我就是感性认识QAQ。
接下来用点分治加速找答案和移动的过程。因为这棵树是静态的,可以一开始先进行一次点分,并记录下每个点到每个分治中心的距离。由于每个点顶多只会被log(n)log(n)个分治中心覆盖,直接开一些nlog(n)nlog(n)的数组即可,这样算答案的时候就不用求树上两点间的路径长度了。求最优点的时候,可以从第一层分治中心向周围的所有点进行试探,看往哪里走更优,然后跳到对应连通块的分治中心去继续找。修改的时候,也只需要改log(n)log(n)个分治中心的信息。所以总时间是O(nlog(n)+qdlog2(n))O(nlog(n)+qdlog2(n))的,实际跑起来很快。
然而tututu有一种更加优美的方法:直接找整棵树的带权重心即为答案。就是找一个点,使得这个点为根时,其子树的最大权值最小。而且这等价于找一个点,使得这个点的子树的最大权值不超过总权值/2。至于正确性证明,tututu说这就是中位数定理在树上的扩展(中位数定理:x轴上有若干点,每个点有权值,要求一个到所有点距离乘以权值最小的点,带权中位数即为答案)。
这题我早上写了2h,然后一交,T了。肉眼debug发现我重心找错了,找了最大子树最大那个点。又交,WA了,然后再debug。一直没找出错,反而找出了很多变量重名的情况,我也不知道编译怎么通过的……自己出了几组小数据,手算了一下过了。上网找tututu的代码,出小数据也对了。无奈之下只好对拍,终于发现了错。主要是因为我naive地以为求出与当前重心相邻的点的答案可以O(1)O(1)算,只要计算对应子连通块的影响就可以了,我debug了好几次都没意识到这样不对。结果最后还是要log(n)log(n)算。这样时间复杂度就多了个log(n)log(n),不过时间好像并没有差很远。归根到底,还是我没有考虑清楚。
CODE:
题目分析:这题简直是噩梦,调了我一个下午。
首先考虑暴力怎么做:在树上随机找一个点,然后计算以该点为补给点的答案,并算出它周围的点的答案。如果它周围存在一个点答案更优,就往该点走,直到当前点为最优点就停止。这样一次询问就是n2dn2d的,其中d=20d=20,因为每个点的度数不超过20。
上面这个做法的正确性,可以用一个看上去显然的结论证明:如果当前最优点为root,那么对于两个点u,v,若v在u到root的路径上,则v处算得的答案比u处优。这个结论的证明自己YY一下即可反正我就是感性认识QAQ。
接下来用点分治加速找答案和移动的过程。因为这棵树是静态的,可以一开始先进行一次点分,并记录下每个点到每个分治中心的距离。由于每个点顶多只会被log(n)log(n)个分治中心覆盖,直接开一些nlog(n)nlog(n)的数组即可,这样算答案的时候就不用求树上两点间的路径长度了。求最优点的时候,可以从第一层分治中心向周围的所有点进行试探,看往哪里走更优,然后跳到对应连通块的分治中心去继续找。修改的时候,也只需要改log(n)log(n)个分治中心的信息。所以总时间是O(nlog(n)+qdlog2(n))O(nlog(n)+qdlog2(n))的,实际跑起来很快。
然而tututu有一种更加优美的方法:直接找整棵树的带权重心即为答案。就是找一个点,使得这个点为根时,其子树的最大权值最小。而且这等价于找一个点,使得这个点的子树的最大权值不超过总权值/2。至于正确性证明,tututu说这就是中位数定理在树上的扩展(中位数定理:x轴上有若干点,每个点有权值,要求一个到所有点距离乘以权值最小的点,带权中位数即为答案)。
这题我早上写了2h,然后一交,T了。肉眼debug发现我重心找错了,找了最大子树最大那个点。又交,WA了,然后再debug。一直没找出错,反而找出了很多变量重名的情况,我也不知道编译怎么通过的……自己出了几组小数据,手算了一下过了。上网找tututu的代码,出小数据也对了。无奈之下只好对拍,终于发现了错。主要是因为我naive地以为求出与当前重心相邻的点的答案可以O(1)O(1)算,只要计算对应子连通块的影响就可以了,我debug了好几次都没意识到这样不对。结果最后还是要log(n)log(n)算。这样时间复杂度就多了个log(n)log(n),不过时间好像并没有差很远。归根到底,还是我没有考虑清楚。
CODE:
#include<iostream> #include<string> #include<cstring> #include<cmath> #include<cstdio> #include<cstdlib> #include<stdio.h> #include<algorithm> using namespace std; const int maxn=100100; const int maxl=25; typedef long long LL; struct edge { int obj; LL len; edge *Next; } e[maxn<<1]; edge *head[maxn]; int cur=-1; int Fa[maxn][maxl]; LL Dep[maxn][maxl]; int Id[maxn][maxl]; int num[maxn]; LL Len[maxn][maxl]; int Son[maxn][maxl]; int cnt[maxn]; int Size[maxn]; int max_Size[maxn]; int tree[maxn]; int Ts; LL sum[maxn][maxl]; LL val[maxn][maxl]; bool vis[maxn]; LL a[maxn]; int n,q; void Add(int x,int y,LL z) { cur++; e[cur].obj=y; e[cur].len=z; e[cur].Next=head[x]; head[x]=e+cur; } void Dfs1(int node,int fa) { tree[++Ts]=node; Size[node]=1; max_Size[node]=0; for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if ( son==fa || vis[son] ) continue; Dfs1(son,node); Size[node]+=Size[son]; max_Size[node]=max(max_Size[node],Size[son]); } } void Dfs2(int node,int fa,LL dep,int id,int mid) { ++num[node]; Fa[node][ num[node] ]=mid; Dep[node][ num[node] ]=dep; Id[node][ num[node] ]=id; for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if ( son==fa || vis[son] ) continue; Dfs2(son,node,dep+p->len,id,mid); } } void Solve(int node) { Ts=0; Dfs1(node,node); if (Ts==1) return; int root=tree[1],tp=Size[node]; for (int i=1; i<=Ts; i++) { int x=tree[i]; Size[x]=max(max_Size[x],tp-Size[x]); //!!! if (Size[x]<Size[root]) root=x; //!!! } for (edge *p=head[root]; p; p=p->Next) { int son=p->obj; if (vis[son]) continue; ++cnt[root]; Son[root][ cnt[root] ]=son; Len[root][ cnt[root] ]=p->len; Dfs2(son,root,p->len,cnt[root],root); } vis[root]=true; for (int i=1; i<=cnt[root]; i++) Solve(Son[root][i]); } void Work(int node,LL v) { a[node]+=v; for (int i=1; i<=num[node]; i++) { int x=Id[node][i]; int root=Fa[node][i]; sum[root][x]+=(v*Dep[node][i]); val[root][x]+=v; sum[root][0]+=(v*Dep[node][i]); val[root][0]+=v; } } LL Calc(int node) { LL temp=0; for (int i=1; i<=num[node]; i++) { int root=Fa[node][i]; int x=Id[node][i]; temp+=(sum[root][0]-sum[root][x]); temp+=( (val[root][0]-val[root][x]+a[root])*Dep[node][i] ); } temp+=sum[node][0]; return temp; } LL Find(int node,int x) { if (Fa[node][x]) node=Fa[node][x]; LL temp=Calc(node); LL ans=temp; int y=node; for (int i=1; i<=cnt[node]; i++) { int son=Son[node][i]; LL now=Calc(son); if (now<=ans) ans=now,y=son; } if (y==node) return temp; else return Find(y,x+1); } int main() { freopen("3345.in","r",stdin); freopen("3345.out","w",stdout); scanf("%d%d",&n,&q); for (int i=1; i<=n; i++) head[i]=NULL; for (int i=1; i<n; i++) { int A,b,c; scanf("%d%d%d",&A,&b,&c); //!!! Add(A,b,c); Add(b,A,c); } Solve(1); for (int i=1; i<=q; i++) { int u,e; scanf("%d%d",&u,&e); Work(u,e); printf("%I64d\n", Find(1,1) ); } return 0; }
相关文章推荐
- bzoj3924 [Zjoi2015]幻想乡战略游戏(动态点分治)
- [BZOJ3924][ZJOI2015]幻想乡战略游戏(动态点分治)
- BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)
- BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)
- bzoj3924: [Zjoi2015]幻想乡战略游戏 //动态点分治
- BZOJ.3924.[ZJOI2015]幻想乡战略游戏(动态点分治)
- 洛谷P3345 [ZJOI2015]幻想乡战略游戏(动态点分治,树的重心,二分查找,Tarjan-LCA,树上差分)
- 【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治
- 【ZJOI 2015 幻想乡战略游戏】【动态点分治】
- [ZJOI2015][bzoj3924] 幻想乡战略游戏 [动态点分治]
- [BZOJ3924][Zjoi2015]幻想乡战略游戏(动态点分治)
- 【BZOJ3924】【Zjoi2015】幻想乡战略游戏(树链剖分+点分治)
- NKOJ 3254 (ZJOI 2015)幻想乡战略游戏(点分治)
- 【BZOJ3924】幻想乡战略游戏(动态点分治)
- 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治
- 3924: [Zjoi2015]幻想乡战略游戏(点分树)
- BZOJ3924: [Zjoi2015]幻想乡战略游戏
- bzoj 3924: [Zjoi2015]幻想乡战略游戏 动态树分治
- bzoj 3924: [Zjoi2015]幻想乡战略游戏
- [BZOJ3924][ZJOI2015]幻想乡战略游戏-动态树分治