正题
题目链接:https://www.luogu.com.cn/problem/P3345
题目大意
nnn个点的一棵树,每次修改一个点的点权后询问一个xxx最小化∑y=1ndis(x,y)∗dy\sum_{y=1}^ndis(x,y)*d_yy=1∑ndis(x,y)∗dy
解题思路
先是构建一个点分树,然后考虑如何计算答案。
我们定义一个frxfr_xfrx表示点分树上faxfa_xfax到xxx所在子树的路径上第一个节点,我们可以比较frxfr_xfrx的答案和faxfa_xfax的答案,如果frxfr_xfrx更大,那么就向xxx移动。那么如何计算两个节点的答案的,我们需要维护三个值sdx,sxx,sfxsd_x,sx_x,sf_xsdx,sxx,sfx分别表示(下面的子树都是点分子树)xxx子树内的点权和,xxx子树内的∑y=1ndis(x,y)∗dy\sum_{y=1}^ndis(x,y)*d_y∑y=1ndis(x,y)∗dy,xxx子树内的所有点∑y=1ndis(fax,y)∗dy\sum_{y=1}^ndis(fa_x,y)*d_y∑y=1ndis(fax,y)∗dy。
这样我们就可以通过枚举到根节点的路径计算每个点的答案,时间复杂度O(nlog3n)O(n\log^3 n)O(nlog3n)(因为有LCALCALCA求路径长度)。
考虑优化,我们可以用RMQRMQRMQ求LCALCALCA,每次到一个点时序列中加入这个点,然后回退时加入父节点,之后询问一个区间中深度最小的节点即可。
时间复杂度O(nlog2n)O(n\log^2 n)O(nlog2n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#define mp(x,y) make_pair(x,y)
#define ll long long
using namespace std;
const int N=2e5+10,T=22;
struct node{int to,next,w;
}a[N*2];
int n,m,tot,cnt,ls[N],dep[N],dis[N],f[N][T],lg[N],dfn[N],rfn[N];
int num,root,f0[N],siz[N],fa[N],sd[N],fr[N];ll sw[N],sx[N];
bool v[N];vector<int> e[N];stack<int> s;
//struct heap{
// priority_queue<pair<int,int> > q1,q2;
// void push(pair<int,int> x)
// {q1.push(x);return;}
// void pop(pair<int,int> x)
// {q2.push(x);return;}
// pair<int,int> top(){
// while(!q2.empty()&&q1.top()==q2.top())
// q1.pop(),q2.pop();
// return q1.top();
// }
// int size(){return q1.size()-q2.size();}
//}q[N];
void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
void dfs(int x,int fa){dfn[++cnt]=x;rfn[x]=cnt;dep[x]=dep[fa]+1;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;dis[y]=dis[x]+a[i].w;dfs(y,x);dfn[++cnt]=x;}return;
}
void init(){dfs(1,1);for(int i=1;i<=cnt;i++)f[i][0]=dfn[i];for(int i=2;i<=cnt;i++)lg[i]=lg[i/2]+1;for(int j=1;(1<<j)<=cnt;j++)for(int i=1;i+(1<<j)-1<=cnt;i++){int x=f[i][j-1],y=f[i+(1<<j-1)][j-1];f[i][j]=dep[x]<dep[y]?x:y;}return;
}
int LCA(int x,int y){int l=rfn[x],r=rfn[y];if(l>r)swap(l,r);int z=lg[r-l+1];x=f[l][z];y=f[r-(1<<z)+1][z];return dep[x]<dep[y]?x:y;
}
int get_dis(int x,int y)
{return dis[x]+dis[y]-2*dis[LCA(x,y)];}
void groot(int x,int fa){siz[x]=1;f0[x]=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||v[y])continue;groot(y,x);siz[x]+=siz[y];f0[x]=max(f0[x],siz[y]);}f0[x]=max(f0[x],num-siz[x]);if(f0[x]<f0[root])root=x;return;
}
void build(int x){v[x]=1;int S=num;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(v[y])continue;num=(siz[y]>siz[x])?(S-siz[x]):(siz[y]);root=0;groot(y,x);fr[root]=y;y=root;build(y);fa[y]=x;e[x].push_back(y);}return;
}
ll count(int x){ll ans=sx[x];for(int y=x;fa[y];y=fa[y])ans+=1ll*(sd[fa[y]]-sd[y])*get_dis(x,fa[y])+sx[fa[y]]-sw[y];return ans;
}
int main()
{freopen("tree1.in","r",stdin);scanf("%d%d",&n,&m);for(int i=1;i<n;i++){int x,y,w;scanf("%d%d%d",&x,&y,&w);addl(x,y,w);addl(y,x,w);}init();num=n;f0[0]=n;groot(1,1);int pr=root;build(root);while(m--){int x,w,y;scanf("%d%d",&x,&w);y=x;while(x){ll z=1ll*w*get_dis(y,fa[x]?fa[x]:x);sw[x]+=z;sx[fa[x]]+=z;sd[x]+=w;x=fa[x];}x=pr;ll ans=count(x);while(1){if(e[x].empty())break;ll z=0;bool flag=0;ans=count(x);for(int i=0;i<e[x].size();i++)if((z=count(fr[e[x][i]]))<ans){x=e[x][i];flag=1;break;}if(flag)continue;break;}ans=count(x);printf("%lld\n",ans);}return 0;
}