CF938G Shortest Path Queries

题目
首先我们想想不加边删边怎么做。
先随便找一棵生成树,然后枚举每条边,把新生成的环的长度放到线性基里。
对于每次询问,把树上路径放到线性基里查询最小值。
如果有加边,每次加边就好了。
想想对于删边应该如何做。
我们把每次询问加一个时间,那么我们每条边都有一个存在时间段。
对于每次询问,只有时间段包含这个时间点的边才能产生贡献。
那么我们考虑对于时间也就是询问开线段树,把每条边按存在时间段加进去,每个节点开一个vector保存边。
显然每条边最多在线段树上的\(log\)个区间内。
然后我们按线段树从上往下遍历也就是按时间分治,每次先把这个时间段的边加入(加入法则就是上面的没有删边的那里),如果到了叶子节点也就是询问就在线性基中查询。
最后撤回在这个时间段的所有加边,我们可以使用支持撤销的并查集来维护。
然后注意一下线性基,线性基需要删除元素,这个似乎不是很好做。
所以我们只需要分治的时候将线性基向下传递即可(或者每个节点开个线性基也没问题)。

#include<bits/stdc++.h>
#define P pair<int,int>
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int N=200007;
int read(){int x;scanf("%d",&x);return x;}
void swap(int &a,int &b){a^=b^=a^=b;}
int n,m,Q,st[N<<1],ed[N<<1],qu[N],qv[N],fa[N],dis[N],dep[N];
struct node{int u,v,w;}e[N<<1];
struct Node{int u,v,f;};
struct lb
{
    int v[31];
    void insert(int x){for(int i=30;~i;--i)if(x&1<<i){if(!v[i])return (void)(v[i]=x);x^=v[i];}}
    int query(int x){for(int i=30;~i;--i)if((x^v[i])<x)x^=v[i];return x;}
}a;
map<P,int>Mp;
vector<node>vec[N<<2];
int Find(int x){return x==fa[x]? x:Find(fa[x]);}
int Getd(int x){return x==fa[x]? 0:dis[x]^Getd(fa[x]);}
void update(int p,int l,int r,int L,int R,node x)
{
    if(L<=l&&r<=R) return (void)vec[p].pb(x);
    if(L<=mid) update(ls,l,mid,L,R,x);
    if(R>mid) update(rs,mid+1,r,L,R,x);
}
void solve(int p,int l,int r,lb a)
{
    stack<Node>stk;
    int i,u,v,w,fu,fv;Node t;
    for(i=0;i<vec[p].size();++i)
    {
    u=vec[p][i].u,v=vec[p][i].v,w=vec[p][i].w^Getd(u)^Getd(v),fu=Find(u),fv=Find(v);
        if(fu==fv) a.insert(w);
        else
        {
            if(dep[fu]>dep[fv]) swap(fu,fv),swap(u,v);
            t=(Node){fu,fv,0},fa[fu]=fv,dis[fu]=w;
            if(dep[fu]==dep[fv]) ++dep[fv],t.f=1;
            stk.push(t);
        }
    }
    if(l==r) printf("%d\n",a.query(Getd(qu[l])^Getd(qv[l])));
    else solve(ls,l,mid,a),solve(rs,mid+1,r,a);
    while(!stk.empty()) dis[fa[stk.top().u]=stk.top().u]=0,dep[stk.top().v]-=stk.top().f,stk.pop();
}
int main()
{
    int i,u,v,w,opt,cnt,T;
    n=read(),m=cnt=read(),T=1;
    for(i=1;i<=n;++i) fa[i]=i;
    for(i=1;i<=m;++i) u=read(),v=read(),w=read(),Mp[mp(u,v)]=i,st[i]=1,ed[i]=-1,e[i]=(node){u,v,w};
    Q=read();
    for(i=1;i<=Q;++i)
    {
        opt=read(),u=read(),v=read();
        if(opt==1) w=read(),Mp[mp(u,v)]=++cnt,st[cnt]=T,ed[cnt]=-1,e[cnt]=(node){u,v,w};
        else if(opt==2) ed[Mp[mp(u,v)]]=T-1;
        else qu[T]=u,qv[T]=v,++T;
    }
    for(--T,i=1;i<=cnt;++i) if(ed[i]==-1) ed[i]=T;
    for(i=1;i<=cnt;++i) if(st[i]<=ed[i]) update(1,1,T,st[i],ed[i],e[i]);
    solve(1,1,T,a);
}

转载于:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11523515.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值