题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2566
题意:一棵有边权的树。结点有颜色。每次修改一个点的颜色。求每次修改后所有同色结点的最近距离。
思路:整体是树分治的方法。其实,分治之后,我们可以理解为重构了这棵树,使得最大深度最小。这棵树的每个结点对于每种颜色保存两个值。一个是该种颜色的所有点到该结点的距离,设这些距离中最小的两个值Min1,Min2,那么Min1+Min2可看做是经过当前结点的这种颜色的两个结点的最近距离,另外,还要保存其他孩子中关于这种颜色的最小值。所有这些最小值的最小值和Min1+Min2再取最小值就是以该节点为根的树的这种颜色的最小值。这些最小值用multiset维护。
另外,结点维护所有同种颜色的距离时也用multiset维护。
对于修改操作,设u节点的颜色由c1变为c2,分两步完成,第一步删除u结点的c1,第二步加入u结点的c2。
两种操作类似,都是从当前结点一直向上更新。细节比较多。
multiset直接删除一个值时是将所有这个值都删掉,只删除一个的话要用指针。
const int M=12005;
struct node
{
int v,w,next;
};
node edges[M<<1];
int head[M],eNum;
void add(int u,int v,int w)
{
edges[eNum].v=v;
edges[eNum].w=w;
edges[eNum].next=head[u];
head[u]=eNum++;
}
int n,m;
int color[M];
int fa[M][20];
int d[M],dep[M];
int visit[M];
queue<int> Q;
void init()
{
Q.push(1);
visit[1]=1;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for(int i=head[u];i!=-1;i=edges[i].next)
{
int v=edges[i].v;
int w=edges[i].w;
if(!visit[v])
{
fa[v][0]=u;
d[v]=d[u]+w;
dep[v]=dep[u]+1;
Q.push(v);
visit[v]=1;
}
}
}
for(int i=1;i<20;i++) for(int j=1;j<=n;j++)
{
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
int calLca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
int x=dep[u]-dep[v];
for(int i=0;i<20;i++) if(x&(1<<i)) u=fa[u][i];
if(u==v) return u;
for(int i=19;i>=0;i--)
{
if(fa[u][i]&&fa[v][i]&&fa[u][i]!=fa[v][i])
{
u=fa[u][i];
v=fa[v][i];
}
}
return fa[u][0];
}
int calDis(int u,int v)
{
int lca=calLca(u,v);
return d[u]+d[v]-d[lca]*2;
}
int sonNum[M];
int nodeSum;
int arr[M],arrNum;
void dfs(int u,int pre)
{
arr[++arrNum]=u;
nodeSum++;
sonNum[u]=1;
for(int i=head[u];i!=-1;i=edges[i].next)
{
int v=edges[i].v;
if(v!=pre&&!visit[v])
{
dfs(v,u);
sonNum[u]+=sonNum[v];
}
}
}
int calCenter(int u)
{
nodeSum=0; arrNum=0;
dfs(u,0);
int ans=u,Min=INF;
for(int i=1;i<=arrNum;i++)
{
int u=arr[i];
int tmp=max(sonNum[u],nodeSum-sonNum[u]);
if(tmp<Min) Min=tmp,ans=u;
}
return ans;
}
struct NODE
{
multiset<int> S,p;
int getAns()
{
if(p.size()<2) return INF;
int Min1=*p.begin();
multiset<int>::iterator it=p.begin();
it++;
int Min2=*it;
return Min1+Min2;
}
void del(int x)
{
p.erase(p.find(x));
}
};
map<int,NODE> mp[M];
map<int,NODE>::iterator it;
int inq[M],KK;
int dis[M];
int parent[M];
int DFS(int root)
{
root=calCenter(root);
Q.push(root);
KK++;
inq[root]=KK;
dis[root]=0;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
int c=color[u];
if(mp[root].count(c))
{
mp[root][c].p.insert(dis[u]);
}
else
{
NODE tmp;
tmp.p.insert(dis[u]);
mp[root][c]=tmp;
}
for(int i=head[u];i!=-1;i=edges[i].next)
{
int v=edges[i].v;
int w=edges[i].w;
if(!visit[v]&&KK!=inq[v])
{
inq[v]=KK;
dis[v]=dis[u]+w;
Q.push(v);
}
}
}
for(it=mp[root].begin();it!=mp[root].end();it++)
{
NODE tmp=it->second;
it->second.S.insert(tmp.getAns());
}
visit[root]=1;
for(int i=head[root];i!=-1;i=edges[i].next)
{
int v=edges[i].v;
if(!visit[v])
{
v=DFS(v);
for(it=mp[v].begin();it!=mp[v].end();it++)
{
int c=it->first;
int w=*(it->second.S.begin());
mp[root][c].S.insert(w);
}
parent[v]=root;
}
}
return root;
}
int root;
multiset<int> SS;
void Add(int u,int c,int dis)
{
if(mp[u].count(c))
{
mp[u][c].p.insert(dis);
}
else
{
NODE tmp;
tmp.p.insert(dis);
mp[u][c]=tmp;
}
}
int st[M],stTop;
void setDel(multiset<int> &S,int x)
{
multiset<int>::iterator it=S.find(x);
if(it!=S.end()) S.erase(it);
}
void del(int u,int c)
{
setDel(SS,*mp[root][c].S.begin());
int curNode=u;
stTop=0;
while(curNode) st[++stTop]=curNode,curNode=parent[curNode];
for(int i=stTop;i>1;i--)
{
int u=st[i];
int v=st[i-1];
setDel(mp[u][c].S,*mp[v][c].S.begin());
}
curNode=u;
while(curNode)
{
int p=parent[curNode];
setDel(mp[curNode][c].S,mp[curNode][c].getAns());
mp[curNode][c].del(calDis(u,curNode));
mp[curNode][c].S.insert(mp[curNode][c].getAns());
if(p)
{
mp[p][c].S.insert(*(mp[curNode][c].S.begin()));
}
curNode=p;
}
SS.insert(*mp[root][c].S.begin());
}
void upd(int u,int c)
{
if(mp[root].count(c))
{
setDel(SS,*mp[root][c].S.begin());
}
int curNode=u;
stTop=0;
while(curNode) st[++stTop]=curNode,curNode=parent[curNode];
for(int i=stTop;i>1;i--)
{
int u=st[i];
int v=st[i-1];
if(!mp[v].count(c)) break;
setDel(mp[u][c].S,*mp[v][c].S.begin());
}
for(int i=1;i<=stTop;i++)
{
int u=st[i];
if(mp[u].count(c))
{
setDel(mp[u][c].S,mp[u][c].getAns());
}
}
curNode=u;
while(curNode)
{
Add(curNode,c,calDis(u,curNode));
mp[curNode][c].S.insert(mp[curNode][c].getAns());
int p=parent[curNode];
if(p)
{
mp[p][c].S.insert(*(mp[curNode][c].S.begin()));
}
curNode=p;
}
SS.insert(*mp[root][c].S.begin());
}
void change(int u,int c)
{
if(color[u]==c) return;
del(u,color[u]);
upd(u,c);
color[u]=c;
}
int main()
{
n=myInt();
for(int i=1;i<=n;i++) color[i]=myInt();
clr(head,-1);
for(int i=1;i<n;i++)
{
int u=myInt();
int v=myInt();
int w=myInt();
add(u,v,w);
add(v,u,w);
}
init();
clr(visit,0);
root=DFS(1);
for(it=mp[root].begin();it!=mp[root].end();it++)
{
SS.insert(*(it->second.S.begin()));
}
int minDis=*SS.begin();
printf("%d\n",minDis==INF?-1:minDis);
m=myInt();
while(m--)
{
int x=myInt();
int y=myInt();
change(x,y);
minDis=*SS.begin();
printf("%d\n",minDis==INF?-1:minDis);
}
}