NOIP2017模擬 玩遊戲
題目大意
給出一個n個節點m條邊的無向連通圖,定義兩點間最短路徑的長度爲所有路徑中最長邊權值的最小值。現在有一些加邊操作和詢問操作,共計q次。詢問操作是要求判斷兩組點對間的最短距離是否相等(原題中以最基本的Nim遊戲的形式給出)。
數據範圍
對於90%的數據,n<=5000,m<=100000,q<=150000,邊權<=1e15,加邊操作不超過1000次。
另有10%數據,每次加邊的權值相同且已知,加邊操作不超過5000次,n=1000,m=100000,q=100000。(也就是特殊處理即可)
原本的題目描述不是很容易讀,這裏簡化了。如果能看出來這樣的“最短距離”就是最小生成樹上兩點路徑中邊權的最大值,那麼本題就基本上做完了。
樹上討論兩點間的路徑果斷LCA,至於邊權最大值顯然可以用倍增處理。建好初始的生成樹時間複雜度
如何處理加邊操作?我的方法可能不夠優秀,在考場上由於一個優化沒加只有70分。加了優化之後勉強能過。
如果暴力重新建樹,每次加邊操作時間複雜度都是
首先,Kruskal算法的時間複雜度
然而這樣還是過不了。有一個顯然的優化:如果目前的最小生成樹中,最長邊的權值都比新加的邊要小,那麼加這條邊顯然是沒有意義的。加了這個優化似乎就能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");
}
}
}