年前的坑今天补……
求一棵树的带权重心,支持修改权值。
动态树分治,也叫点分树。
就是把每层的重心连成一棵树,然后在这棵树上乱搞(具体网上教程多)。
不过第一次写这题暴力碾过去了…..好像还挺快的….
先讲暴力
假设上一次找到的重心在u,那么如果在某一点v增加了权值,那当前的重心一定是在u到v的相反方向上,只要沿着相反方向找就行了。
具体怎么找…可以这么想:
当前结点为x,y为与x相邻的结点,w[x]为x结点上的权值,cnt为总权值,那么如果cnt-w[y]< w[y]即cnt<2*w[y]时,往y点移动,直到不能移动为止。至于为什么……自己脑补一下
w[x]可以用dfs序加BIT维护。
#include
#include
#include
#include
#define inf 1ll<<60
#define N 100010
using namespace std;
typedef long long ll;
int G[N],n,m,tt,rt,l[N],r[N],tc,lcA[N][25],dp[N];
ll B[N<<1],Ans,A[N],tot,ds[N];
struct edge{
int w,t,nx;
}E[N<<1];
struct lef{
int f,w,d;
}T[N];
inline char C(){
static char buf[100000],*p1=buf,*p2=buf;
if(p1==p2){
p2=(p1=buf)+fread(buf,1,100000,stdin);
if(p1==p2) return EOF;
}
return *p1++;
}
inline void reaD(int &x){
char Ch=C();x=0;int f=1;
for(;Ch>'9'||Ch<'0';Ch=C())if(Ch=='-')f=-1;
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=C());x*=f;
}
inline void reaD(ll &x){
char Ch=C();x=0;ll f=1;
for(;Ch>'9'||Ch<'0';Ch=C())if(Ch=='-')f=-1;
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=C());x*=f;
}
inline void InserT(int x,int y,int w){
E[++tt].t=y;E[tt].nx=G[x];E[tt].w=w;G[x]=tt;
E[++tt].t=x;E[tt].nx=G[y];E[tt].w=w;G[y]=tt;
}
inline void build(int g,int f,int d){
T[g].f=f;l[g]=++tc;T[g].d=d;
dp[g]=dp[f]+1;ds[g]=ds[f]+d;
lcA[g][0]=f;
for(int i=1;i<=20;i++) lcA[g][i]=lcA[lcA[g][i-1]][i-1];
for(int i=G[g];i;i=E[i].nx)
if(E[i].t!=f) build(E[i].t,g,E[i].w);
r[g]=++tc;
}
inline void add(int x,int y){
for(;x<=tc;x+=x&-x)
B[x]+=y;
}
inline ll query(int x){
ll res=0;
for(;x;x-=x&-x) res+=B[x];
return res;
}
inline ll Qlw(int x){
return query(r[x])-query(l[x]);
}
int w[30],wt;
inline void Pt(ll x){
if(!x){putchar(48);putchar('\n');return;}
if(x<0){putchar('-');x=-x;};
while(x)w[++wt]=x%10,x/=10;
for(;wt;wt--)putchar(48+w[wt]);putchar('\n');
}
inline void swap(int &x,int &y){
int z=x;x=y;y=z;
}
inline int clca(int x,int y){
if(dp[x]int delt=dp[x]-dp[y],i;
for(i=0;i<=20;i++)
if(delt&(1<while(x!=y){
for(i=-1;i;i++) if(lcA[x][i+1]==lcA[y][i+1]) break;
if(i==-1) return lcA[x][0];
x=lcA[x][i];y=lcA[y][i];
}
return x;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
reaD(n);reaD(m);
for(int i=1,x,y,w;i/*int ok=0;
for(int i=1;i<=n;i++) if(du[i]>2){ok=1;break;}
if(!ok){linktime();return 0;}*/
build(1,0,0);reaD(rt);
reaD(A[rt]);tot+=A[rt];
add(r[rt],A[rt]);
Pt(Ans=0);
for(int i=1,x,y,j,lca;i1ll*(ds[x]+ds[rt]-2*ds[lca])*y;
//S(rt,0,Ans);
while(1){
if(T[rt].f&&tot>2ll*Qlw(rt)){Ans-=1ll*(tot-2*Qlw(rt))*T[rt].d;rt=T[rt].f;continue;}
for(j=G[rt];j;j=E[j].nx)
if(T[rt].f!=E[j].t&&tot<2ll*Qlw(E[j].t)){
Ans-=1ll*(2*Qlw(E[j].t)-tot)*E[j].w;
rt=E[j].t;
break;
}
if(!j) break;
}
Pt(Ans);
}
return 0;
}
复杂度是 O(λnlogn) , λ 为一个常数(由数据决定),由于随机数据,所以 λ 较小,所以复杂度竟然比标算小……
正确做法是点分树。
建出点分树,每次找只要从根节点开始分治地找就行了。
复杂度 O(nlog2n)
#include
#define N 100010
typedef long long ll;
int n,m,maxs,root,sizz,trot;
ll Anst,Ans,nAns;
int nG[N],G[N],cnt,V[N],p[N],dfslt[N<<2],lca[N<<2][20],dept[N],pst[N],tw[N<<2],ben[N];
ll w[N],dist[N],subd[N],dis2[N];
struct edge{
int t,nx,t1;
ll w;
}nE[N<<2],E[N<<2];
inline char C(){
static char buf[100000],*p1=buf,*p2=buf;
if(p1==p2){
p2=(p1=buf)+fread(buf,1,100000,stdin);
if(p1==p2) return EOF;
}
return *p1++;
}
inline void reaD(int &x){
char Ch=C();x=0;int f=1;
for(;Ch>'9'||Ch<'0';Ch=C())if(Ch=='-')f=-1;
for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=C());x*=f;
}
inline void InsEdge(int u,int v,int w){
nE[++cnt].t=v;nE[cnt].nx=nG[u];nE[cnt].w=w;nG[u]=cnt;
nE[++cnt].t=u;nE[cnt].nx=nG[v];nE[cnt].w=w;nG[v]=cnt;
}
inline int max(const int &a,const int &b){
return avoid Getsz(int x,int f){
sizz++;
for(int i=nG[x];i;i=nE[i].nx)
if(!V[nE[i].t]&&nE[i].t!=f) Getsz(nE[i].t,x);
}
int GetRoot(int x,int f){
int mx=0,sz=1,nsz;
for(int i=nG[x];i;i=nE[i].nx){
int t=nE[i].t;
if(V[t]||t==f) continue;
nsz=GetRoot(t,x);
mx=max(mx,nsz);
sz+=nsz;
}
mx=max(mx,sizz-sz);
if(mxreturn sz;
}
inline void AddEdge(int x,int y,int z){
E[++cnt].t=y;E[cnt].nx=G[x];E[cnt].t1=z;G[x]=cnt;
}
void swap(int &x,int &y){
int z=x;x=y;y=z;
}
int LCA(int x,int y){
int a=pst[x],b=pst[y];
if(a>b) swap(a,b);
int t=tw[b-a+1];
return dept[lca[a][t]]1<1][t]]?lca[a][t]:lca[b-(1<1][t];
}
int divont(int x,int f){
sizz=0,maxs=1<<30,Getsz(x,0),GetRoot(x,0);
int nRoot=root,nxRoot;V[nRoot]=1;p[nRoot]=f;
for(int i=nG[nRoot];i;i=nE[i].nx)
if(nE[i].t!=f&&!V[nE[i].t]){
nxRoot=divont(nE[i].t,nRoot);
AddEdge(nRoot,nxRoot,nE[i].t);
}
return nRoot;
}
void dfs(int x,int f){
dept[x]=dept[f]+1;dfslt[pst[x]=++cnt]=x;
for(int i=nG[x];i;i=nE[i].nx)
if(nE[i].t!=f)dist[nE[i].t]=dist[x]+nE[i].w,dfs(nE[i].t,x),dfslt[++cnt]=x;
}
void Prelca(){
for(int i=1;i<=cnt;i++) tw[i]=tw[i-1]+((1<1]+1)==i);
for(int i=1;i<=cnt;i++) lca[i][0]=dfslt[i];
for(int k=1;k<=tw[cnt];k++)
for(int i=1;i+(1<1<=cnt;i++)
lca[i][k]=dept[lca[i][k-1]]1<1)][k-1]]?lca[i][k-1]:lca[i+(1<1)][k-1];
}
ll dis(int x,int y){
return dist[x]+dist[y]-2*dist[LCA(x,y)];
}
inline void Addtr(int x,int y){
w[x]+=y;
for(int i=x,j;p[i];i=p[i]){
w[p[i]]+=y;
subd[p[i]]+=dis(x,p[i])*y;
dis2[i]+=dis(x,p[i])*y;
}
}
inline ll min(const ll &a,const ll &b){
return ainline ll disf(int x){
ll re=subd[x];
for(int i=x;p[i];i=p[i]){
ll disr=dis(x,p[i]);
re+=subd[p[i]]-dis2[i];
re+=(w[p[i]]-w[i])*disr;
}
return re;
}
inline ll query(int x){
bool flg=1;
ll tot=disf(x);
for(int i=G[x];i;i=E[i].nx){
ll cost=disf(E[i].t1);
if(costreturn query(E[i].t);
}
return tot;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
reaD(n);reaD(m);
for(int i=1,u,v,w;i0,dfs(1,0),Prelca(),
cnt=0,trot=divont(1,0),cnt=0;
for(int i=1;i<=m;i++){
int x,y;
reaD(x);reaD(y);
Ans=(Anst+=y*dis(x,trot));
Addtr(x,y),cnt+=y;
printf("%lld\n",query(trot));
}
}
事实证明如果考场上想不出标算或没时间写标算,这种信仰暴力还是可以接受的……