逛森林
这是一道模板题
首先,对任意时刻,
那么这里利用倍增优化建边
倍增的分治结构是什么?
这个分治的状态是不是就出来了?注意绿边的边权为0
比如说我现在有一个传送门,是从
那么这个的复杂度是多少?首先考虑树上的边,总共是
考虑优化,利用st表的思想,对一条长度为
这两个区间代表的节点进行连边就好了
其他的优化建边
update 2024.5.12
我们来说一下代码的细节
首先,千万不要认为我们对每个点都会建一颗有
然后,在求连边的节点时,不要用st表的写法(预处理出Log数组然后求出最大长度),因为这不是链,而是树;我们应该直接对代码中LCA的部分进行改动(然而这样的话时间复杂度就没办法优化了)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e4+10,M=1e6+10,INF=0x3f3f3f3f;
int n,m,s,cnt,tot;
int fa[N];
int End[M<<5],Len[M<<5],Last[M<<2],Next[M<<5],dis[M<<2];
int dep[N],f[N][20],in[N][20],out[N][20];
bool vis[M<<2],mark[M];
int u[M][3],v[M][3],w[M],op[M];
vector<int> G[N];
struct Node
{
int Num,dis;
bool operator<(const Node &a) const
{
return a.dis<dis;
}
};
priority_queue<Node> q;
int getfa(int x)
{
return fa[x]==x?x:fa[x]=getfa(fa[x]);
}
void merge(int x,int y)
{
fa[getfa(x)]=getfa(y);
}
void add(int x,int y,int z)
{
End[++tot]=y,Len[tot]=z,Next[tot]=Last[x],Last[x]=tot;
}
void init(int x,int fa)
{
dep[x]=dep[fa]+1;
for(int i=0;i<=17;i++)
{
f[x][i+1]=f[f[x][i]][i];
in[x][i+1]=++cnt,add(cnt,in[x][i],0),add(cnt,in[f[x][i]][i],0);
out[x][i+1]=++cnt,add(out[x][i],cnt,0),add(out[f[x][i]][i],cnt,0);
//in表示出点,out表示入点
}
for(int i=1;i<=G[x].size();i++)
{
int v=G[x][i-1];
if(v!=fa)
{
f[v][0]=x;
in[v][0]=++cnt,add(cnt,v,0),add(cnt,x,0);
out[v][0]=++cnt,add(v,cnt,0),add(x,cnt,0);
init(v,x);
}
}
}
void LCA(int ver,int id)//直接对LCA的代码进行改动连边
{
int x=u[ver][id],y=v[ver][id];
if(x==y)
{
if(id==1) add(x,cnt,0);
else add(cnt,x,0);
return;
}
if(dep[x]<dep[y]) swap(x,y);
for(int i=17;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y])
{
if(id==1) add(out[x][i],cnt,0);
else add(cnt,in[x][i],0);
x=f[x][i];
}
if(x==y) return;
if(dep[x]==dep[y]) break;
}
for(int i=17;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
if(id==1) add(out[x][i],cnt,0),add(out[y][i],cnt,0);
else add(cnt,in[x][i],0),add(cnt,in[y][i],0);
x=f[x][i];
y=f[y][i];
}
}
if(id==1) add(out[x][0],cnt,0),add(out[y][0],cnt,0);
else add(cnt,in[x][0],0),add(cnt,in[y][0],0);
return;
}
void Dij()
{
Node temp;
temp.Num=s;
temp.dis=0;
dis[s]=0;
q.push(temp);
while(!q.empty())
{
int u=q.top().Num;
q.pop();
if(vis[u]==1) continue;
vis[u]=1;
for(int i=Last[u];i;i=Next[i])
{
int v=End[i];
if(dis[v]>dis[u]+Len[i])
{
dis[v]=dis[u]+Len[i];
temp.Num=v;
temp.dis=dis[v];
q.push(temp);
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
memset(dis,0x3f,sizeof(dis));
cnt=n;//cnt表示节点总数
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&op[i],&u[i][1],&v[i][1]);
if(op[i]==1)
{
scanf("%d%d%d",&u[i][2],&v[i][2],&w[i]);
mark[i]=1;//mark表示这次操作是否有效
for(int j=1;j<=2;j++)
if(getfa(u[i][j])!=getfa(v[i][j]))
{
mark[i]=0;
break;
}
}
else
{
scanf("%d",&w[i]);
if(getfa(u[i][1])!=getfa(v[i][1]))
{
mark[i]=1;
merge(u[i][1],v[i][1]);
G[u[i][1]].push_back(v[i][1]);
G[v[i][1]].push_back(u[i][1]);
//注意树中的边一定要和最终图的边分开存
//如果存一起的话
//在init过程中,由于我们要加边
//就会把边加进去,破坏树的结构
}
}
}
for(int i=1;i<=n;i++)
if(!dep[i]) init(i,0);
//注意森林的每一颗树都要init
//因为加了传送门之后可能连通
for(int i=1;i<=m;i++)
if(op[i]==1&&mark[i])
{
for(int j=1;j<=2;j++)
{
cnt++;//创建虚拟节点
LCA(i,j);
}
add(cnt-1,cnt,w[i]);//两个虚拟节点连边
}
else if(op[i]==2&&mark[i])
add(u[i][1],v[i][1],w[i]),add(v[i][1],u[i][1],w[i]);//注意树中的边也要添加到最终图中
Dij();
for(int i=1;i<=n;i++)
printf("%d ",dis[i]>INF/2?-1:dis[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构