cf div2_199_E

題目的意思是給你一顆樹n(100000)個樹的幾點,然後開始樹都是白色的,根的顏色是紅色的,然後給你m個操作,1  x操作表示把x這個點變成紅色,2 x詢問x到最近紅色點的距離,我聽聞有3種解法(分割+點的分治+輕重邊)

解法1 對於m(100000)個詢問分成sqrt(m) 塊,然後在每個塊內,如果是更改操作,把相應點放入臨時數組,對於查詢,先看原圖最小距離,和臨時數組裏麪點的最小值(通過lca 處理)對於每個塊之間用用一次bfs 把臨時數組裏面的值更新給原圖,可以得出負責度爲(sqrt(m)*n)開始寫的時候無限tle 後面只對於更改分塊,因爲就更改會改變臨時數組的

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#define M 101000
#define inf 0x3f3f3f3f
using namespace std;
struct G{
    int head[M],en;
    struct E{
        int u,v,next;
    }e[M<<2];
    void init(){
        memset(head,-1,sizeof(head));en=0;
    }
    void add(int u,int v){
        e[en].u=u;e[en].v=v;e[en].next=head[u];head[u]=en++;
    }
}g1;
int dex[M*2];
void init_dex(){
    dex[0]=-1;
    for(int i=1;i<M*2-1;i++){
        if(i&(i-1)) dex[i]=dex[i-1];
        else dex[i]=dex[i-1]+1;
    }
}
int aa[M*2],bb[M*2],en;
struct R{
    int head[M*2],raa[M*2][20],rbb[M*2][20];
    void init(){
        memset(rbb,inf,sizeof(rbb));
        memset(head,-1,sizeof(head));
        for(int i=0;i<en;i++){
            int u=aa[i];
            if(head[u]==-1){
                head[u]=i;
            }
            raa[i][0]=aa[i];
            rbb[i][0]=bb[i];
        }
        for(int i=1;(1<<i)<=en;i++){
            for(int j=0;j+(1<<i)<en;j++){
                int next=j+(1<<(i-1));
                if(rbb[j][i-1]<rbb[next][i-1]){
                    rbb[j][i]=rbb[j][i-1];
                    raa[j][i]=raa[j][i-1];
                }
                else{
                    rbb[j][i]=rbb[next][i-1];
                    raa[j][i]=raa[next][i-1];
                }
            }
        }
    }
    int query(int l,int r){
        l=head[l];r=head[r];
        if(l>r) swap(l,r);
        int p1=dex[r-l+1];
        r-=(1<<(p1))-1;
        if(rbb[l][p1]<=rbb[r][p1]) return raa[l][p1];
        return raa[r][p1];
    }
}rmq;
int dis[M];
int is_red[M];
void dfs1(int u,int p,int level){
    dis[u]=level;
    aa[en]=u;
    bb[en++]=level;
    for(int i=g1.head[u];~i;i=g1.e[i].next){
        int v=g1.e[i].v;
        if(v==p) continue;
        dfs1(v,u,level+1);
        aa[en]=u;
        bb[en++]=level;
    }
}
int block[M];
int tmin[M];
int my_queue[M],qh,qt;
void dfs2(int u,int p,int level){
    if(tmin[u]>level) tmin[u]=level;
    else return ;
    for(int i=g1.head[u];~i;i=g1.e[i].next){
        int v=g1.e[i].v;
        if(v==p) continue;
        if(is_red[v]) continue;
        dfs2(v,u,level+1);
    }
}
void bfs(){
    while(qt>=qh){
        int u=my_queue[qh++];
        if(is_red[u]==1) tmin[u]=0;
        for(int i=g1.head[u];~i;i=g1.e[i].next){
            int v=g1.e[i].v;
            if(tmin[v]>tmin[u]+1){
                tmin[v]=tmin[u]+1;
                my_queue[++qt]=v;
            }
        }
    }
}
int main(){
    init_dex();
    int n,m;while(~scanf("%d%d",&n,&m)){
        g1.init();
        for(int i=0;i<n-1;i++){
            int u,v;scanf("%d%d",&u,&v);
            g1.add(u,v);g1.add(v,u);
        }
        en=0;
        dfs1(1,-1,0);
        rmq.init();
        for(int i=1;i<=n;i++){
            tmin[i]=dis[i];
        }
        int num=0;
        int flag=0;
        int siz=200;
        memset(is_red,0,sizeof(is_red));
        is_red[1]=1;
        for(int i=0;i<m;i++){
            int t1,t2;scanf("%d%d",&t1,&t2);
            if(t1==2){
                if(is_red[t2]){
                    puts("0");continue;
                }
                int ans=tmin[t2];
                for(int j=0;j<num;j++){
                    int tt=block[j];
                    int fa=rmq.query(t2,tt);
                    int t3=dis[t2]+dis[tt]-2*dis[fa];
                    if(t3<ans) ans=t3;
                }
                printf("%d\n",ans);
            }
            else{
                flag++;
                is_red[t2]=1;
                block[num++]=t2;
            }
            if(flag==siz){
                qh=0;qt=-1;
                for(int j=0;j<num;j++){
                    int t3=block[j];
                    my_queue[++qt]=t3;
                }
                bfs();
                flag=0;
                num=0;
            }
        }
    }
    return 0;
}

長度,780ms ac這個好像是官方提供的解

解法2 樹點的分治 大神的思路自己模仿代碼

#include<cstdio>
#include<cstring>
#include<iostream>
#define M 111000
#define inf 0x3f3f3f3f
using namespace std;
struct G1{
    int head[M],en;
    struct E{
        int u,v,next,flag;
    }e[M<<4];
    void init(){
        memset(head,-1,sizeof(head));en=0;
    }
    void add(int u,int v){
        e[en].flag=0;
        e[en].u=u;e[en].v=v;e[en].next=head[u];head[u]=en++;
    }
}g1;
struct G2{
    int head[M],en;
    struct E{
        int u,v,next,cost;
    }e[M<<6];
    void init(){
        memset(head,-1,sizeof(head));en=0;
    }
    void add(int u,int v,int cost){
        e[en].cost=cost;
        e[en].u=u;e[en].v=v;e[en].next=head[u];head[u]=en++;
    }
}g2;
int dis[M],cen,ms[M],son[M],siz;
void find_root(int u,int p){
    son[u]=1,ms[u]=0;
    for(int i=g1.head[u];~i;i=g1.e[i].next){
        int v=g1.e[i].v;
        if(v==p||g1.e[i].flag) continue;
        find_root(v,u);
        son[u]+=son[v];
        if(son[v]>ms[u]) ms[u]=son[v];
    }
    if(ms[u]<siz-son[u]) ms[u]=siz-son[u];
    if(ms[u]<ms[cen]){
        cen=u;
    }
}
void dfs(int u,int p,int pos){
    dis[u]=dis[p]+1;
    g2.add(u,pos,dis[u]);
    for(int i=g1.head[u];~i;i=g1.e[i].next){
        int v=g1.e[i].v;
        if(g1.e[i].flag||v==p) continue;
        dfs(v,u,pos);
    }
}
void divide(int pos){
    dis[0]=-1;
    dfs(pos,0,pos);
    for(int i=g1.head[pos];i!=-1;i=g1.e[i].next){
        g1.e[i^1].flag=1;
        if(g1.e[i].flag) continue;
        int v=g1.e[i].v;
        cen=0;ms[cen]=siz=son[v];
        find_root(v,-1);
        divide(cen);
    }
}
int ans[M];
void mark(int u){
    ans[u]=0;
    for(int i=g2.head[u];~i;i=g2.e[i].next){
        int v=g2.e[i].v;
        int cost=g2.e[i].cost;
        if(ans[v]>ans[u]+cost) ans[v]=ans[u]+cost;
    }
}
int query(int u){
    int ret=ans[u];
    for(int i=g2.head[u];~i;i=g2.e[i].next){
        int v=g2.e[i].v;
        int cost=g2.e[i].cost;
        if(ret>ans[v]+cost) ret=ans[v]+cost;
    }
    return ret;
}
int main(){
    int n,m;while(~scanf("%d%d",&n,&m)){
        g1.init();
        for(int i=0;i<n-1;i++){
            int u,v;scanf("%d%d",&u,&v);g1.add(u,v);g1.add(v,u);
        }
        siz=n;cen=0;ms[cen]=siz;
        find_root(1,-1);
        g2.init();
        divide(cen);
        memset(ans,inf,sizeof(ans));
        mark(1);
        for(int i=0;i<m;i++){
            int t1,t2;scanf("%d%d",&t1,&t2);
            if(t1==1){
                mark(t2);
            }
            else{
                int tt=query(t2);
                printf("%d\n",tt);
            }
        }
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章