题意:
给出一颗树,最开始所有点都是白点,在树上维护两种操作:
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
的距离和,以及当前这个树中蓝点总数
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同在一颗子树中的蓝点的影响。最后将当前的询问分配给下一层子树。
因为每个询问只做了次,每次是
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));
}*/