CF938G Shortest Path Queries

本文探讨了线性基和生成树算法在处理特定类型问题中的应用,特别是在包含加边和删边操作的场景中。文章介绍了如何利用线段树分治策略,结合按秩合并的并查集,动态维护生成树,以及使用线性基进行快速查询和更新。通过详细的代码实现,展示了算法的具体运用。
摘要由CSDN通过智能技术生成

首先只有询问的话就是个WC的题,线性基+生成树搞一搞就行。

进一步,考虑如果修改操作只有加边怎么做。
好像也没有什么变化,只不过需要在线地往线性基里插入东西而已。

删边呢?
注意到线性基这个玩意是不支持删除操作的。
对于这种不好删除的的东西有种不错的解决方法,就是线段树分治。
把每个操作劈成logn个区间以后来搞一下。
按照线段树分治的套路,通过遍历整棵线段树来获得答案。
发现需要一个可以动态维护支持加/删边的生成树的东西。
能做到这个的无非就两个数据结构,按秩合并的并查集和LCT。
这里使用按秩合并的并查集。
注意:
这里之所以利用线段树分治的原因是,使用线段树结构可以在每个节点都维护出对应的线性基。
儿子的线性基可以由线段树上的父亲的线性基加上自身节点内的操作来得到,由于线性基很小,可以快速下传,因此可以这么搞。
剩下的就是码码码了

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<map>
#include<vector>
#include<cstdlib>
#include<algorithm>
#define N 440000
#define L 1000000
#define M 6600000
#define eps 1e-7
#define inf 1e9+7
#define db double
#define ll long long
#define ldb long double
using namespace std;
inline int read()
{
    char ch=0;
    int x=0,flag=1;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*flag;
}
struct link{int x,y,z;};
bool operator<(link a,link b){if(a.x!=b.x)return a.x<b.x;return a.y<b.y;}
map<link,int>S;
map<link,int>::iterator it;
int n,m,qnum,now,cnt,tot,ans[N];
struct ques{int x,y,id;}q[N];
struct node{link s;int l,r;}p[N*2];
//find-union-set
int top,f[N],v[N],sz[N],sk[M];
int find(int x){if(x!=f[x])return find(f[x]);else return f[x];}
int value(int x){if(x!=f[x])return value(f[x])^v[x];else return 0;}
bool merge(link s)
{
    int a=s.x,b=s.y,x=find(a),y=find(b),z=s.z;
    if(x==y)return false;   
    if(sz[x]>sz[y])swap(x,y),swap(a,b);
    v[x]=value(a)^value(b)^z;
    f[x]=y;sz[y]+=sz[x];sk[++top]=x;
    return true;
}
void erase()
{
    int x=sk[top--];
    sz[f[x]]-=sz[x];f[x]=x;v[x]=0;
}
//linear basis
struct pot
{
    int s[33];
    void insert(int x)
    {
        for(int i=31;i>=0;i--)if(1<<i&x)
        {
            if(!s[i]){s[i]=x;break;}
            else x^=s[i]; 
        }
    }
    int query(int x)
    {
        for(int i=31;i>=0;i--)if(1<<i&x)x^=s[i];
        return x;
    }
}sp[200];
//Segment_Tree
#define lson o<<1
#define rson o<<1|1
#define mid ((l+r)>>1)
vector<link>V[N*4];
void optset(int o,int l,int r,int ql,int qr,link s)
{
    if(ql<=l&&r<=qr){V[o].push_back(s);return;}
    if(ql<=mid)optset(lson,l,mid,ql,qr,s);
    if(qr>mid)optset(rson,mid+1,r,ql,qr,s);
}
void solve(int o,int l,int r,int t)
{
    if(l>r)return;
    int times=0,len=V[o].size();
    for(int i=0;i<len;i++)
    if(merge(V[o][i]))times++;
    else sp[t].insert(value(V[o][i].x)^value(V[o][i].y)^V[o][i].z);
    if(l==r)
    {
        while(now<tot&&q[now+1].id==l)
        {
            now++;
            ans[q[now].id]=sp[t].query(value(q[now].x)^value(q[now].y));
        }
    }
    else
    {
        for(int i=31;i>=0;i--)sp[t+1].s[i]=sp[t].s[i];solve(lson,l,mid,t+1);
        for(int i=31;i>=0;i--)sp[t+1].s[i]=sp[t].s[i];solve(rson,mid+1,r,t+1);
    }
    for(int i=0;i<times;i++)erase();
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        link o;
        o.x=read();o.y=read();o.z=read();
        if(o.x>o.y)swap(o.x,o.y);
        S.insert(pair<link,int>(o,0));
    }
    qnum=read();
    for(int i=1;i<=qnum;i++)
    {
        int flag=read();
        if(flag==1)
        {
            link o;
            o.x=read();o.y=read();o.z=read();
            if(o.x>o.y)swap(o.x,o.y);
            S.insert(pair<link,int>(o,i));
        }
        if(flag==2)
        {
            link o;
            o.x=read();o.y=read();
            it=S.find(o);
            p[++cnt]=(node){it->first,it->second,i};
            S.erase(it);
        }
        if(flag==3)
        {
            q[++tot].id=i;q[tot].x=read();q[tot].y=read();
            if(q[tot].x>q[tot].y)swap(q[tot].x,q[tot].y);
        }
    }
    for(it=S.begin();it!=S.end();it++)p[++cnt]=(node){it->first,it->second,m};
    for(int i=1;i<=cnt;i++)optset(1,0,qnum,p[i].l,p[i].r,p[i].s);
    for(int i=1;i<=n;i++)f[i]=i,v[i]=0,sz[i]=1;
    solve(1,0,m,1);
    for(int i=1;i<=tot;i++)printf("%d\n",ans[q[i].id]);
    return 0;
}

转载于:https://www.cnblogs.com/Creed-qwq/p/10290279.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值