【HDU】5571 tree【动态点分治】

本文解析了一道关于动态点分治的算法题,通过构建树状结构并使用动态规划来解决复杂查询问题,适用于高级算法竞赛选手学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:【HDU】5571 tree【动态点分治】

题目大意:给一棵 N 节点的树,每个点有点权ai,有 m 次单点点权修改,每次修改以后,输出Ni=1Nj=1(aixoraj)dis(ai,dj)的值。
题目分析:考虑每一位独立做,就是所有 01 对的对数* 01 对的距离,待修改可用动态树分治维护。每个重心树下每个节点保存所属的重心,所属的重心的哪个儿子的子树,到重心的距离。

#include <bits/stdc++.h>
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 30005 ;
const int MAXE = 60005 ;

struct Edge {
    int v , c , n ;
    Edge () {}
    Edge ( int v , int c , int n ) : v ( v ) , c ( c ) , n ( n ) {}
} ;

struct Node {
    int root , f , dep ;
    Node () {}
    Node ( int root , int f , int dep ) : root ( root ) , f ( f ) , dep ( dep ) {}
} ;

Edge E[MAXE] ;
int H[MAXN] , cntE ;
int Q[MAXN] , head , tail ;
vector < Node > G[MAXN] ;
int vis[MAXN] ;
int dep[MAXN] ;
int pre[MAXN] ;
int siz[MAXN] ;

int tree_cnt ;
int pos[MAXN] ;
int val[MAXN] ;
LL dis[MAXN << 1][2] ;
LL num[MAXN << 1][2] ;
LL res[MAXN] ;
int a[MAXN] ;
int n , m ;

void init () {
    cntE = 0 ;
    tree_cnt = 0 ;
    clr ( H , -1 ) ;
    clr ( vis , 0 ) ;
}

void addedge ( int u , int v , int c ) {
    E[cntE] = Edge ( v , c , H[u] ) ;
    H[u] = cntE ++ ;
}

int get_root ( int s ) {
    head = tail = 0 ;
    Q[tail ++] = s ;
    pre[s] = 0 ;
    while ( head != tail ) {
        int u = Q[head ++] ;
        for ( int i = H[u] ; ~i ; i = E[i].n ) {
            int v = E[i].v ;
            if ( vis[v] || v == pre[u] ) continue ;
            pre[v] = u ;
            Q[tail ++] = v ;
        }
    }
    int root = s , root_siz = tail ;
    while ( head ) {
        int u = Q[-- head] , cnt = 0 ;
        siz[u] = 1 ;
        for ( int i = H[u] ; ~i ; i = E[i].n ) {
            int v = E[i].v ;
            if ( vis[v] || v == pre[u] ) continue ;
            siz[u] += siz[v] ;
            if ( cnt < siz[v] ) cnt = siz[v] ;
        }
        cnt = max ( cnt , tail - siz[u] ) ;
        if ( cnt < root_siz ) {
            root_siz = cnt ;
            root = u ;
        }
    }
    return root ;
}

void calc ( int s , int d , int f , int root ) {
    head = tail = 0 ;
    dep[s] = d ;
    pre[s] = 0 ;
    Q[tail ++] = s ;
    while ( head != tail ) {
        int u = Q[head ++] ;
        G[u].push_back ( Node ( root , f , dep[u] ) ) ;
        for ( int i = H[u] ; ~i ; i = E[i].n ) {
            int v = E[i].v ;
            if ( vis[v] || v == pre[u] ) continue ;
            pre[v] = u ;
            dep[v] = dep[u] + E[i].c ;
            Q[tail ++] = v ;
        }
    }
}

void dfs ( int u ) {
    int root = get_root ( u ) ;
    vis[root] = 1 ;
    calc ( root , 0 , 1 , ++ tree_cnt ) ;
    for ( int i = H[root] ; ~i ; i = E[i].n ) {
        if ( !vis[E[i].v] ) calc ( E[i].v , E[i].c , -1 , ++ tree_cnt ) ;
    }
    for ( int i = H[root] ; ~i ; i = E[i].n ) {
        if ( !vis[E[i].v] ) dfs ( E[i].v ) ;
    }
    vis[root] = 0 ;
}

void solve () {
    int u , v , c ;
    init () ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%d" , &a[i] ) ;
        a[i] <<= 1 ;
        G[i].clear () ;
    }
    for ( int i = 1 ; i < n ; ++ i ) {
        scanf ( "%d%d%d" , &u , &v , &c ) ;
        addedge ( u , v , c ) ;
        addedge ( v , u , c ) ;
    }
    scanf ( "%d" , &m ) ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        scanf ( "%d%d" , &pos[i] , &val[i] ) ;
        val[i] <<= 1 ;
        res[i] = 0 ;
    }
    dfs ( 1 ) ;
    for ( int o = 0 ; o < 14 ; ++ o ) {
        for ( int i = 1 ; i <= tree_cnt ; ++ i ) {
            dis[i][0] = dis[i][1] = 0 ;
            num[i][0] = num[i][1] = 0 ;
        }
        LL ans = 0 ;
        for ( int i = 1 ; i <= n ; ++ i ) {
            int v = ( a[i] >>= 1 ) & 1 ;
            for ( int j = 0 ; j < G[i].size () ; ++ j ) {
                Node x = G[i][j] ;
                ans += x.f * ( x.dep * num[x.root][v ^ 1] + dis[x.root][v ^ 1] ) ;
                dis[x.root][v] += x.dep ;
                num[x.root][v] ++ ;
            }
        }
        for ( int i = 1 ; i <= m ; ++ i ) {
            int v1 = a[pos[i]] & 1 ;
            int v2 = ( val[i] >>= 1 ) & 1 ;
            if( v1 != v2 ) {
                for ( int j = 0 ; j < G[pos[i]].size () ; ++ j ) {
                    Node x = G[pos[i]][j] ;
                    ans -= x.f * ( x.dep * num[x.root][v1 ^ 1] + dis[x.root][v1 ^ 1] ) ;
                    dis[x.root][v1] -= x.dep ;
                    num[x.root][v1] -- ;
                    ans += x.f * ( x.dep * num[x.root][v2 ^ 1] + dis[x.root][v2 ^ 1] ) ;
                    dis[x.root][v2] += x.dep ;
                    num[x.root][v2] ++ ;
                }
                a[pos[i]] ^= v1 ^ v2 ;
            }
            res[i] += ans * ( 1 << o ) ;
        }
    }
    for ( int i = 1 ; i <= m ; ++ i ) {
        printf ( "%lld\n" , res[i] ) ;
    }
}

int main () {
    while ( ~scanf ( "%d" , &n ) ) solve () ;  
    return 0 ;
}

压缩后代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

#define clr(a,x) memset(a,x,sizeof a)

const int MAXN=30005;
const int MAXE=60005;

struct Edge{
    int v,c,n;
    Edge(){}
    Edge(int v,int c,int n):v(v),c(c),n(n){}
};

struct Node{
    int rt,f,dep;
    Node(){}
    Node(int rt,int f,int dep):rt(rt),f(f),dep(dep){}
};

Edge E[MAXE];
int H[MAXN],cntE;
int Q[MAXN],head,tail;
vector<Node>G[MAXN];
int vis[MAXN],dep[MAXN],pre[MAXN],siz[MAXN],cnt[MAXN],pos[MAXN],val[MAXN],tcnt;
LL dis[MAXN<<1][2],num[MAXN<<1][2],res[MAXN];
int a[MAXN],n,m;
void init(){
    cntE=tcnt=0;
    clr(H,-1),clr(vis,0);
}
void addedge(int u,int v,int c){
    E[cntE]=Edge(v,c,H[u]);
    H[u]=cntE++;
}
int get_rt(int s){
    int u,v,i,rt=s,l=0,r=0;
    for(l=pre[Q[0]=s]=0,r=1;l!=r;){
        for(v=E[i=H[u=Q[l++]]].v;~i;v=E[i=E[i].n].v)if(!vis[v]&&v!=pre[u]){
            pre[Q[r++]=v]=u;
            siz[v]=1,cnt[v]=0;
        }
    }
    for(int sz=r,u=Q[l-1];l--;u=Q[l-1]){
        siz[pre[u]]+=siz[u];
        if(cnt[pre[u]]<siz[u])cnt[pre[u]]=siz[u];
        cnt[u]=max(cnt[u],r-siz[u]);
        if(cnt[u]<sz)sz=cnt[u],rt=u;
    }
    return rt;
}
void calc(int s,int d,int f,int rt){
    int l,r,u,v,i;
    for(l=r=pre[s]=0,dep[Q[r++]=s]=d;l!=r;){
        G[Q[l]].push_back(Node(rt,f,dep[Q[l]]));
        for(v=E[i=H[u=Q[l++]]].v;~i;v=E[i=E[i].n].v){
            if(!vis[v]&&v!=pre[u])dep[v]=dep[pre[Q[r++]=v]=u]+E[i].c;
        }
    }
}
void dfs(int u){
    int rt=get_rt(u);
    vis[rt]=1;
    calc(rt,0,1,++tcnt);
    for(int i=H[rt];~i;i=E[i].n)if(!vis[E[i].v])calc(E[i].v,E[i].c,-1,++tcnt);
    for(int i=H[rt];~i;i=E[i].n)if(!vis[E[i].v])dfs(E[i].v);
    vis[rt]=0;
}
void solve(){
    int u,v,c;
    init();
    for(int i=1;i<=n;G[i].clear(),a[i]<<=1,++i)scanf("%d",&a[i]);
    for(int i=1;i<n;++i){
        scanf("%d%d%d",&u,&v,&c);
        addedge(u,v,c);
        addedge(v,u,c);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;val[i]<<=1,res[i]=0,++i)scanf("%d%d",&pos[i],&val[i]);
    dfs(1);
    for(int o=0;o<14;++o){
        for(int i=1;i<=tcnt;++i){
            dis[i][0]=dis[i][1]=0;
            num[i][0]=num[i][1]=0;
        }
        LL ans=0;
        for(int i=1;i<=n;++i){
            int v=(a[i]>>=1)&1;
            for(int j=0;j<G[i].size();++j){
                Node x=G[i][j];
                ans+=x.f*(x.dep*num[x.rt][v^1]+dis[x.rt][v^1]);
                dis[x.rt][v]+=x.dep;
                num[x.rt][v]++;
            }
        }
        for(int i=1;i<=m;++i){
            int v1=a[pos[i]]&1;
            int v2=(val[i]>>=1)&1;
            if(v1!=v2){
                for(int j=0;j<G[pos[i]].size();++j){
                    Node x=G[pos[i]][j];
                    ans-=x.f*(x.dep*num[x.rt][v1^1]+dis[x.rt][v1^1]);
                    dis[x.rt][v1]-=x.dep;
                    num[x.rt][v1]--;
                    ans+=x.f*(x.dep*num[x.rt][v2^1]+dis[x.rt][v2^1]);
                    dis[x.rt][v2]+=x.dep;
                    num[x.rt][v2]++;
                }
                a[pos[i]]^=v1^v2;
            }
            res[i]+=ans*(1<<o);
        }
    }
    for(int i=1;i<=m;++i){
        printf("%lld\n",res[i]);
    }
}
int main(){
    while(~scanf("%d",&n))solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值