【点分治】SRM624 TreeColoring

题意:

给出一颗树,最开始所有点都是白点,在树上维护两种操作:
1、将某个点染为蓝点
2、询问所有蓝点到某个点的距离和


分析:

根据点分治算法的核心思想:每次处理经过重心的问题,再分治;
我们可以把每个询问离线操作,在每一层,我们处理操作位置在当前联通块内的询问。
这样一来,每个询问只会被执行 log n l o g   n 次。
对于统计答案,我们对每一层进行一次DFS,统计所有点到当前重心 (u) ( u ) 的距离 dist(u,i) d i s t ( u , i ) ,以及该点是在重心哪条邻接边上(设为 fa(i) f a ( i ) ),以重心为根,在与重心相邻的每个点维护该点所在的子树内,所有蓝点到u的距离 (sum(x)) ( s u m ( x ) ) (注意,是到重心u的距离,不是到x的距离) ,以及这棵子树内蓝点的数量 (num(x)) ( n u m ( x ) )
最后,还要维护所有点到重心 u u 的距离和(sum(u)),以及当前这个树中蓝点总数 num(u) n u m ( u )
按顺序处理每一个询问(设当前操作的点为 v v ),如果为1操作:
根据我们维护的值的定义:
num(fa(v))++,num(u)++,sum(fa(v))+=dist(v,u),sum(u)+=dist(v,u)

如果为2操作:
ans[i]+=sum(u)-sum(fa(v))+dist(v,u)*(num(u)-num(fa(v)))
这样一来,我们就清除了所有与v同在一颗子树中的蓝点的影响。最后将当前的询问分配给下一层子树。
因为每个询问只做了log n次,每次是 O(1) O ( 1 ) 完成,对于每一层,我们还需要 O(n) O ( n ) 的DFS预处理,整体复杂度 O(nlog n) O ( n l o g   n )
所以最终时间复杂度为 O(nlog n) O ( n l o g   n ) ,但实测发现似乎并没有这么优秀,可能是常数过大。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
int startSeed,maxDist,threshold;
int N,Q;
int distancex[MAXN],parent[MAXN],queryType[MAXN],queryNode[MAXN];
int curValue;

int genNextRandom() {
    curValue = (curValue * 1999 + 17) % 1000003;
    return curValue;
}

void generateInput() {
    curValue = startSeed;
    for (int i = 0; i < N-1; i++) {
        distancex[i] = genNextRandom() % maxDist;
        parent[i] = genNextRandom();
        if (parent[i] < threshold) {
            parent[i] = i;
        } else {
            parent[i] = parent[i] % (i + 1);
        }
    }

    for (int i = 0; i < Q; i++) {
        queryType[i] = genNextRandom() % 2 + 1;
        queryNode[i] = genNextRandom() % N;
    }
}
struct node{
    int id,tag,pos;
}que[MAXN];
vector<node> q[MAXN];
vector<int> a[MAXN],b[MAXN],w[MAXN];
long long dep[MAXN],ans[MAXN],sum[MAXN];
int typ[MAXN],num[MAXN];
bool used[MAXN],vis[MAXN];
int res1,sz[MAXN],maxs[MAXN],ytoid[MAXN];
void dfs(int x,int fa){
    sz[x]=1;
    maxs[x]=0;
    for(int i=0;i<a[x].size();i++)
        if(a[x][i]!=fa&&used[a[x][i]]==0){
            dfs(a[x][i],x);
            sz[x]+=sz[a[x][i]];
            maxs[x]=max(maxs[x],sz[a[x][i]]);
        }
}
void dfs(int x,int fa,int sum){
    maxs[x]=max(maxs[x],sum-sz[x]);
    if(maxs[x]<maxs[res1]||res1==0)
        res1=x;
    for(int i=0;i<a[x].size();i++)
        if(a[x][i]!=fa&&used[a[x][i]]==0)
            dfs(a[x][i],x,sum);
}
int find_gra(int x){
    dfs(x,0);
    res1=0;
    dfs(x,0,sz[x]);
    return res1;
}
void prepare(int x,int fa,int anc,long long deep){
    dep[x]=deep;
    typ[x]=anc;
    for(int i=0;i<a[x].size();i++)
        if(a[x][i]!=fa&&used[a[x][i]]==0)
            prepare(a[x][i],x,anc,deep+w[x][i]);
}
void solve(int x){
    used[x]=1;
    for(int i=0;i<a[x].size();i++)
        if(used[a[x][i]]==0){
            b[x].push_back(find_gra(a[x][i]));
            ytoid[a[x][i]]=b[x].size()-1;
        }
    for(int i=0;i<a[x].size();i++){
        if(used[a[x][i]]==1)
            continue;
        sum[a[x][i]]=0;
        num[a[x][i]]=0;
        prepare(a[x][i],x,a[x][i],w[x][i]);
    }
    sum[x]=0;
    num[x]=0;
    for(int i=0;i<q[x].size();i++){
        int id=q[x][i].id;
        int pos=q[x][i].pos;
        int tag=q[x][i].tag;
        if(pos==x){
            if(tag==1)
                num[x]++;
            if(tag==2)
                ans[id]+=sum[x];
            continue;
        }
        int y=typ[pos];
        if(tag==1){
            sum[x]+=dep[pos];
            sum[y]+=dep[pos];
            num[x]++;
            num[y]++;
        }
        else
            ans[id]+=sum[x]-sum[y]+1ll*(num[x]-num[y])*dep[pos];
        q[b[x][ytoid[y]]].push_back(q[x][i]);
    }
    for(int i=0;i<b[x].size();i++)
        solve(b[x][i]);
}
class TreeColoring{
public:
    long long color(int N1,int Q1,int startSeed1,int threshold1,int maxDist1){
        N=N1,Q=Q1,startSeed=startSeed1,threshold=threshold1,maxDist=maxDist1;
        generateInput();
        int cnt=0;
        for(int i=0;i<Q;i++){
            if(queryType[i]==1&&vis[queryNode[i]]==1)
                continue;
            if(queryType[i]==1)
                vis[queryNode[i]]=1;
            que[cnt].pos=queryNode[i]+1;
            que[cnt].tag=queryType[i];
            que[cnt].id=cnt;
            cnt++;
        }
        Q=cnt;
        for(int i=2;i<=N;i++){
            int u=i,v=parent[i-2],val=distancex[i-2];
            v++;
            a[u].push_back(v);
            a[v].push_back(u);
            w[u].push_back(val);
            w[v].push_back(val);
        }
        int x=find_gra(1);
        for(int i=0;i<Q;i++)
            q[x].push_back(que[i]);
        solve(x);
        long long res=0;
        for(int i=0;i<Q;i++)
            res^=ans[i];
        return res;
    }
};
/*int main(){
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
    int r1,r2,r3,r4,r5;
    SF("%d%d%d%d%d",&r1,&r2,&r3,&r4,&r5);
    PF("%lld",sbcch.color(r1,r2,r3,r4,r5));
}*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值