[BZOJ3924][Zjoi2015][点分树][暴力]幻想乡战略游戏

来源:互联网 发布:公务员考试试题软件 编辑:程序博客网 时间:2024/06/04 18:47

年前的坑今天补……


题意


求一棵树的带权重心,支持修改权值。


动态树分治,也叫点分树。
就是把每层的重心连成一棵树,然后在这棵树上乱搞(具体网上教程多)。

不过第一次写这题暴力碾过去了…..好像还挺快的….

先讲暴力
假设上一次找到的重心在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 <cstdio>#include <iostream>#include <string>#include <cstring>#define inf 1ll<<60#define N 100010using 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]<dp[y]) swap(x,y);    int delt=dp[x]-dp[y],i;    for(i=0;i<=20;i++)        if(delt&(1<<i)) x=lcA[x][i];    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<n;i++)reaD(x),reaD(y),reaD(w),InserT(x,y,w);    /*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;i<m;i++){        reaD(x);reaD(y);        add(r[x],y);A[x]+=y;tot+=y;        lca=clca(x,rt);Ans+=1ll*(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 <cstdio>#define N 100010typedef 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 a<b?b:a;}void 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(mx<maxs) maxs=mx,root=x;    return 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]]<dept[lca[b-(1<<t)+1][t]]?lca[a][t]:lca[b-(1<<t)+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<<tw[i-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<<k)-1<=cnt;i++)        lca[i][k]=dept[lca[i][k-1]]<dept[lca[i+(1<<k-1)][k-1]]?lca[i][k-1]:lca[i+(1<<k-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 a<b?a:b;}inline 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(cost<tot) return 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;i<n;i++)reaD(u),reaD(v),reaD(w),InsEdge(u,v,w);    cnt=0,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));    }}

事实证明如果考场上想不出标算或没时间写标算,这种信仰暴力还是可以接受的……

0 0
原创粉丝点击