NOIP2017模擬 玩遊戲 最小生成樹 樹上倍增

NOIP2017模擬 玩遊戲

題目大意

給出一個n個節點m條邊的無向連通圖,定義兩點間最短路徑的長度爲所有路徑中最長邊權值的最小值。現在有一些加邊操作和詢問操作,共計q次。詢問操作是要求判斷兩組點對間的最短距離是否相等(原題中以最基本的Nim遊戲的形式給出)。

數據範圍

對於90%的數據,n<=5000,m<=100000,q<=150000,邊權<=1e15,加邊操作不超過1000次。

另有10%數據,每次加邊的權值相同且已知,加邊操作不超過5000次,n=1000,m=100000,q=100000。(也就是特殊處理即可)


原本的題目描述不是很容易讀,這裏簡化了。如果能看出來這樣的“最短距離”就是最小生成樹上兩點路徑中邊權的最大值,那麼本題就基本上做完了。

樹上討論兩點間的路徑果斷LCA,至於邊權最大值顯然可以用倍增處理。建好初始的生成樹時間複雜度O(mlogm) ,預處理出倍增數組的時間複雜度O(nlogn) ,如果沒有加邊操作,詢問可以在O(logn) 內完成。

如何處理加邊操作?我的方法可能不夠優秀,在考場上由於一個優化沒加只有70分。加了優化之後勉強能過。

如果暴力重新建樹,每次加邊操作時間複雜度都是O(mlogm+nlogn) ,這樣顯然是過不了的,考慮優化。

首先,Kruskal算法的時間複雜度O(mlogm) 是因爲排序耗了太多時間,如果邊已經排好序了,剩下就可以在線性時間內得到答案。每次我們只是加入一個數,而之前的數組都是排好序的,所以插入排序就可以把加邊時的Kruskal的時間複雜度降爲O(m)

然而這樣還是過不了。有一個顯然的優化:如果目前的最小生成樹中,最長邊的權值都比新加的邊要小,那麼加這條邊顯然是沒有意義的。加了這個優化似乎就能AC了。

如果用LCT那這道題就是大水題,可惜當時不會。


不優秀的代碼:

#include<stdio.h>
#include<algorithm>
#include<cstring>
#define MAXN 5005
#define MAXM 400005
#define ll long long
using namespace std;

int N,M,TOT;
ll MaxLen;

struct edge{int St,En;ll Len;}E[MAXM];
bool operator<(edge a,edge b){return a.Len<b.Len;}

int tot,en[MAXM],nex[MAXM],las[MAXN];
ll len[MAXM];
void Add(int x,int y,ll z)
{
    en[++tot]=y;
    nex[tot]=las[x];
    las[x]=tot;
    len[tot]=z;
}

/*-----------------------MST--------------------------*/

void Ins(int a,int b,ll c)//插入排序
{
    int i;

    TOT++;
    E[TOT].St=a;E[TOT].En=b;E[TOT].Len=c;

    for(i=TOT-1;i;i--)
    {
        if(E[i+1]<E[i])swap(E[i],E[i+1]);
        else return;
    }
}

int Fa[MAXN];
int gf(int x)
{
    if(Fa[x]!=x)Fa[x]=gf(Fa[x]);
    return Fa[x];
}

void Kruskal()
{
    int i,x,y,cnt=0;

    for(i=1;i<=N;i++)Fa[i]=i;

    memset(las,0,sizeof(las));
    tot=0;

    for(i=1;i<=TOT&&cnt!=N-1;i++)
    {
        x=E[i].En;y=E[i].St;
        x=gf(x);y=gf(y);
        if(x==y)continue;

        Add(E[i].En,E[i].St,E[i].Len);
        Add(E[i].St,E[i].En,E[i].Len);
        MaxLen=max(MaxLen,E[i].Len);

        cnt++;
        Fa[x]=y;
    }
}

/*-------------------------LCA-----------------------*/

int fa[MAXN][14],dep[MAXN];
ll v[MAXN][14];

void DFS(int x,int f,ll z)
{
    int i,y;

    fa[x][0]=f;
    dep[x]=dep[f]+1;
    v[x][0]=z;

    for(i=1;i<14;i++)fa[x][i]=fa[fa[x][i-1]][i-1],v[x][i]=max(v[fa[x][i-1]][i-1],v[x][i-1]);
    for(i=las[x];i;i=nex[i])
    {
        y=en[i];
        if(y==f)continue;
        DFS(y,x,len[i]);
    }
}

ll LCA(int x,int y)
{
    int i,d,t;
    ll ans=0;

    if(dep[x]<dep[y])t=x,x=y,y=t;
    d=dep[x]-dep[y];
    for(i=0;i<14;i++)if((d>>i)&1)ans=max(ans,v[x][i]),x=fa[x][i];
    if(x==y)return ans;
    for(i=13;i>=0;i--)
    if(fa[x][i]!=fa[y][i])
    {
        ans=max(ans,v[x][i]);ans=max(ans,v[y][i]);
        x=fa[x][i];y=fa[y][i];
    }
    ans=max(v[x][0],ans);ans=max(ans,v[y][0]);
    return ans;
}

int main()
{
    int i,x,y,cnt=0;
    ll z;
    char op[6];

    scanf("%d%d",&N,&M);
    TOT=M;

    for(i=1;i<=M;i++)
    {
        scanf("%d%d%lld",&x,&y,&z);
        E[i].St=x;E[i].En=y;E[i].Len=z;
    }

    sort(E+1,E+M+1);

    Kruskal();
    DFS(1,0,0);

    int Q,m1,m2,b1,b2;
    ll vb,vm;

    scanf("%d",&Q);

    while(Q--)
    {
        scanf("%s",op);
        if(op[0]=='a')
        {
            scanf("%d%d%lld",&x,&y,&z);

            cnt++;
            if(cnt>1000||MaxLen<z)continue;
            //cnt>1000是爲了解決特判的數據點。這裏我懶得專門寫一種情況了
            Ins(x,y,z);
            Kruskal();
            DFS(1,0,0);
        }
        else
        {
            scanf("%d%d%d%d",&m1,&m2,&b1,&b2);
            vm=LCA(m1,m2);
            vb=LCA(b1,b2);
            if(vb!=vm)puts("madoka");
            else puts("Baozika");
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章