题目大意:给定一张带权无向图,每个点有一个颜色,每次改变一个点的颜色,要求你在操作后输出这个图中最近异色点对之间的距离
最近异色点对定义为:一对点颜色不同,且距离最小
老年选手码什么数据结构 老老实实退役得了
结论1:答案一定是一条边的两端点
证明:假如答案路径的边数
≥2
,任取路径中间一点,显然它与路径的两端点中至少一个异色(因为两端点异色),故答案可以变得更小
结论2:答案边一定在最小生成树上
证明:假如答案边不在最小生成树上,那么最小生成树上两点之间的路径上一定能找到一条合法的答案边,且权值不会比它大
有了这两个结论其实很好办了。我们建出最小生成树,然后每个点开一个以颜色为下标的线段树,维护颜色在对应区间的所有子节点离该节点距离的最小值。由于同一颜色的子节点可能有多个,因此要在线段树的叶节点处开个multiset存该颜色的所有子节点到该节点的距离,以便删除
然后我们就能统计出每个点到最近异色子节点的距离(异色处理方法:假如颜色为
c
,那么异色就是
具体细节自己YY吧 思路大概就是这样
#include <set>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;
int n,m,k,q;
int col[M],a[M],fa[M];
int last_ans[M];
multiset<int> ans;
struct Segtree
{
static vector<Segtree*> bin;
union{
Segtree *ls;
multiset<int> *val;
};
Segtree *rs;
int min_x;
void* operator new (size_t)
{
static Segtree *S,*T;
Segtree *re;
if(!bin.empty())
{
re=bin.back();
bin.pop_back();
}
else
{
#define L (1<<16)
if(S==T)
T=(S=new Segtree[L])+L;
re=S++;
}
re->ls=re->rs=0x0;
re->min_x=0x3f3f3f3f;
return re;
}
void operator delete (void *p)
{
bin.push_back((Segtree*)p);
}
void Update()
{
#define MIN(p) ((p)?(p)->min_x:0x3f3f3f3f)
min_x=min(MIN(ls),MIN(rs));
}
friend void Insert(Segtree *&p,int l,int r,int pos,int val)
{
int mid=l+r>>1;
if(!p) p=new Segtree;
if(l==r)
{
if(!p->val)
p->val=new multiset<int>;
p->val->insert(val);
p->min_x=*(p->val->begin());
return ;
}
if(pos<=mid)
Insert(p->ls,l,mid,pos,val);
else
Insert(p->rs,mid+1,r,pos,val);
p->Update();
}
friend void Delete(Segtree *&p,int l,int r,int pos,int val)
{
int mid=l+r>>1;
if(l==r)
{
p->val->erase(p->val->find(val));
if(p->val->empty())
{
delete p->val;
delete p;
p=0x0;
}
else
p->min_x=*(p->val->begin());
return ;
}
if(pos<=mid)
Delete(p->ls,l,mid,pos,val);
else
Delete(p->rs,mid+1,r,pos,val);
p->Update();
if(p->min_x==0x3f3f3f3f)
{
delete p;
p=0x0;
}
}
friend int Query(Segtree *p,int l,int r,int x,int y)
{
int mid=l+r>>1;
if(!p) return 0x3f3f3f3f;
if(x==l&&y==r)
return p->min_x;
if(y<=mid)
return Query(p->ls,l,mid,x,y);
if(x>mid)
return Query(p->rs,mid+1,r,x,y);
return min(Query(p->ls,l,mid,x,mid),Query(p->rs,mid+1,r,mid+1,y));
}
}*root[M];
vector<Segtree*> Segtree::bin;
struct edge
{
int x,y,z;
friend bool operator < (const edge &e1,const edge &e2)
{
return e1.z < e2.z;
}
}edges[M];
struct abcd{
int to,f,next;
}table[M<<1];
int head[M],tot;
void Add(int x,int y,int z)
{
table[++tot].to=y;
table[tot].f=z;
table[tot].next=head[x];
head[x]=tot;
}
namespace Disjoint_Set
{
int fa[M];
int Find(int x)
{
if(!fa[x]||fa[x]==x)
return fa[x]=x;
return fa[x]=Find(fa[x]);
}
void Union(int x,int y)
{
x=Find(x);y=Find(y);
fa[x]=y;
}
}
void Kruskal()
{
using namespace Disjoint_Set;
sort(edges+1,edges+m+1);
for(int i=1;i<=m;i++)
{
int x=Find(edges[i].x);
int y=Find(edges[i].y);
if(x==y)
continue;
Add(edges[i].x,edges[i].y,edges[i].z);
Add(edges[i].y,edges[i].x,edges[i].z);
Union(x,y);
}
}
void BFS()
{
static int q[M];
static bool v[M];
int r=0,h=0;
q[++r]=1;v[1]=true;
while(r!=h)
{
int x=q[++h];
for(int i=head[x];i;i=table[i].next)
if(!v[table[i].to])
{
fa[table[i].to]=x;
a[table[i].to]=table[i].f;
v[table[i].to]=true;
q[++r]=table[i].to;
}
}
}
int Query(int x)
{
int re=0x3f3f3f3f;
if(col[x]!=1)
re=min(re,Query(root[x],1,k,1,col[x]-1));
if(col[x]!=k)
re=min(re,Query(root[x],1,k,col[x]+1,k));
return re;
}
int main()
{
cin>>n>>m>>k>>q;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&edges[i].x,&edges[i].y,&edges[i].z);
Kruskal();
for(int i=1;i<=n;i++)
scanf("%d",&col[i]);
BFS();
for(int i=2;i<=n;i++)
Insert(root[fa[i]],1,k,col[i],a[i]);
for(int i=1;i<=n;i++)
ans.insert(last_ans[i]=Query(i));
for(int i=1,x,y;i<=q;i++)
{
scanf("%d%d",&x,&y);
if(fa[x])
Delete(root[fa[x]],1,k,col[x],a[x]);
col[x]=y;
if(fa[x])
Insert(root[fa[x]],1,k,col[x],a[x]);
ans.erase(ans.find(last_ans[x]));
ans.insert(last_ans[x]=Query(x));
if(fa[x])
{
ans.erase(ans.find(last_ans[fa[x]]));
ans.insert(last_ans[fa[x]]=Query(fa[x]));
}
printf("%d\n",*ans.begin());
}
return 0;
}