bzoj4777 [Usaco2017 Open]Switch Grass MST+线段树+multiset

Description


给定一张带权无向图,每个点有一个颜色,每次改变一个点的颜色,要求你在操作后输出这个图中最近异色点对之间的距离
最近异色点对定义为:一对点颜色不同,且距离最小

Solution


容易想到答案一定在最小生成树上,并且只可能是最小生成树上的某一条边
那么可以对每个节点以颜色为下标建线段树,线段树的叶子节点用multiset记录节点所代表颜色出现的距离,再用一个全局multiset记录全局最小值,就能做到在线修改+查询惹

一开始错是因为对于一条边(x,y,w)而言w被算重了。一个不算重的方法就是每个节点只记录它的儿子,那么每次修改就只需要修改父亲+查询儿子

然后错是因为并不是每个节点都需要用一个multiset,只需要开够叶子节点数量个就行了

接着错是因为可能出现root[x]为0的情况,这样是查询不到东西的

最后错是因为常数巨大,松了松很多次之后才勉强做到9400ms。期间改动的优化包括但不限于:重复利用数组、减少for的数量、记录lastans、输出优化(最不实用

我这么弱也是没有办法(摊手

Code


#include 
#include 
#include 
#include 
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const int INF=0x3f3f3f3f;
const int N=200005;
const int E=200005;

struct edge {int y,w,next;} e[E*2];
struct rec {int x,y,w;} r[N];
struct treeNode {int l,r,id,min;} t[N*29];

std:: multiset <int> set[N*3],glo;

int root[N],tot;
int ls[N],edCnt,cnt;
int fa[N],a[N],len[N];
int lastans[N];
int n,m,k,T;

bool is_leaf[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch>='0'&&ch<='9';x=(x<<1)+(x<<3)+(ch-'0'),ch=getchar());
    return x*v;
}

void writeln(int x){
    char ch[21]={}; int i=0;
    if (x<0) putchar('-');
    do {ch[++i]='0'+x%10;} while (x/=10);
    while (i) putchar(ch[i--]);
    putchar('\n');
}

void add_edge(int x,int y,int w) {
    e[++edCnt]=(edge) {y,w,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge) {x,w,ls[y]}; ls[y]=edCnt;
}

int get_father(int x) {
    if (fa[x]==x) return x;
    return fa[x]=get_father(fa[x]);
}

bool cmp(rec a,rec b) {
    return a.wvoid modify(int &now,int tl,int tr,int x,int v,int opt) {
    if (!now) t[now=++tot].min=INF;
    if (tl==tr) {
        if (!t[now].id) t[now].id=++cnt;
        int id=t[now].id;
        if (opt==0) set[id].erase(set[id].find(v));
        else set[id].insert(v);
        if (set[id].empty()) t[now].min=INF;
        else t[now].min=*(set[id].begin());
        return ;
    }
    int mid=(tl+tr)>>1;
    if (x<=mid) modify(t[now].l,tl,mid,x,v,opt);
    else modify(t[now].r,mid+1,tr,x,v,opt);
    t[now].min=std:: min(t[t[now].l].min,t[t[now].r].min);
}

int query(int now,int tl,int tr,int l,int r) {
    if (!now||rreturn INF;
    if (tl==l&&tr==r) return t[now].min;
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(t[now].l,tl,mid,l,r);
    if (l>mid) return query(t[now].r,mid+1,tr,l,r);
    int qx=query(t[now].l,tl,mid,l,mid);
    int qy=query(t[now].r,mid+1,tr,mid+1,r);
    return std:: min(qx,qy);
}

void dfs(int now) {
    is_leaf[now]=1;
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==fa[now]) continue;
        modify(root[now],1,k,a[e[i].y],e[i].w,1);
        len[e[i].y]=e[i].w;
        fa[e[i].y]=now;
        dfs(e[i].y);
        is_leaf[now]=0;
    }
}

int main(void) { t[0].min=INF;
    // freopen("data.in","r",stdin);
    // freopen("myp.out","w",stdout);
    n=read(),m=read(),k=read(),T=read();
    rep(i,1,m) r[i]=(rec) {read(),read(),read()};
    rep(i,1,n) fa[i]=i;
    std:: sort(r+1,r+m+1,cmp);
    int count=0;
    rep(i,1,m) {
        int fx=get_father(r[i].x);
        int fy=get_father(r[i].y);
        if (fx!=fy) {
            fa[fx]=fy;
            add_edge(r[i].x,r[i].y,r[i].w);
            if (++count==n-1) break;
        }
    }
    rep(i,1,n) {
        a[i]=read();
        fa[i]=0;
    }
    dfs(1);
    rep(i,1,n) {
        if (is_leaf[i]) continue;
        int qx=query(root[i],1,k,1,a[i]-1);
        int qy=query(root[i],1,k,a[i]+1,k);
        lastans[i]=std:: min(qx,qy);
        glo.insert(lastans[i]);
    }
    for (;T--;) {
        int x=read(),v=read();
        if (x!=1) {
            int up=fa[x];
            glo.erase(glo.find(lastans[up]));
            modify(root[up],1,k,a[x],len[x],0);
            modify(root[up],1,k,v,len[x],1);
            int qx=query(root[up],1,k,1,a[up]-1);
            int qy=query(root[up],1,k,a[up]+1,k);
            lastans[up]=std:: min(qx,qy);
            glo.insert(lastans[up]);
        }
        if (root[x]) {
            glo.erase(glo.find(lastans[x]));
            int qx=query(root[x],1,k,1,v-1);
            int qy=query(root[x],1,k,v+1,k);
            lastans[x]=std:: min(qx,qy);
            glo.insert(lastans[x]);
        }
        a[x]=v; int prt=*glo.begin();
        writeln(prt);
    }
    return 0;
}

你可能感兴趣的:(c++,线段树,stl,最小生成树)