CF938G Shortest Path Queries

一、题目

点此看题

二、解法

首先做一下这道题:最大XOR和路径,是这题的弱化版。

首先介绍一下线段树分治,就是把每一个修改的影响的询问区间打到线段树上(类比线段树区间修改),然后再访问一遍线段树,期间加入在这个点上的修改,到 l = r l=r l=r时就可以拿到询问的值了。

具体可以维护一个并查集,我们不能路径压缩,但是可以通过启发式合并来保证复杂度。如果 ( u , v ) (u,v) (u,v)是联通的话,那么就加入这个环,否则启发式合并,合并 ( f u , f v ) (fu,fv) (fu,fv)时边权设置为 c ⊕ d i s ( f u , u ) ⊕ d i s ( f v , v ) c\oplus dis(fu,u)\oplus dis(fv,v) cdis(fu,u)dis(fv,v),那么 v v v f u fu fu的距离就正好是 c ⊕ d i s ( f u , u ) c\oplus dis(fu,u) cdis(fu,u),然后我们还需要写一个回退(不难)

询问的话用线性基,方法就是弱化版的方案,贴个代码。

#include <cstdio>
#include <iostream>
#include <vector>
#include <stack>
#include <map>
using namespace std;
const int M = 200005;
#define mp make_pair
int read()
{	
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,k,q,tot,bg[2*M],ed[2*M],qu[M],qv[M],fa[M],dep[M],dis[M];
map<pair<int,int> ,int> Mp;
struct edge
{
	int u,v,c;
}e[2*M];vector<edge> tr[M<<2];
struct node
{
	int w[40];
	void insert(int x)
	{
		for(int i=30;i>=0;i--)
		{
			if(!(x>>i)) continue;
			if(!w[i]){w[i]=x;return ;}
			x^=w[i];
		}
	}
	int query(int x)
	{
		for(int i=30;i>=0;i--)
			if((x^w[i])<x) x^=w[i];
		return x;
	}
}a;
void ins(int i,int l,int r,int L,int R,edge e)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) 
		return (void)tr[i].push_back(e);
	int mid=(l+r)>>1;
	ins(i<<1,l,mid,L,R,e);
	ins(i<<1|1,mid+1,r,L,R,e);
}
int find(int x)
{
	if(x==fa[x]) return x;
	return find(fa[x]);
}
int get(int x)
{
	if(x==fa[x]) return 0;
	return dis[x]^get(fa[x]);
}
void cdq(int p,int l,int r,node a)
{
	stack<edge> s;edge t;
	for(int i=0;i<tr[p].size();i++)
	{
		int u=tr[p][i].u,v=tr[p][i].v,c=tr[p][i].c^get(u)^get(v);
		int fu=find(u),fv=find(v);
		if(fu==fv)
			a.insert(c);
		else
		{
			if(dep[fu]>dep[fv]) swap(fu,fv),swap(u,v);
			t=edge{fu,fv,0},fa[fu]=fv,dis[fu]=c;
			if(dep[fu]==dep[fv]) dep[fv]++,t.c=1;
			s.push(t);
		}
	}
	int mid=(l+r)>>1;
	if(l==r) printf("%d\n",a.query(get(qu[l])^get(qv[l])));
	else cdq(p<<1,l,mid,a),cdq(p<<1|1,mid+1,r,a);
	while(!s.empty())
		dis[fa[s.top().u]=s.top().u]=0,dep[s.top().v]-=s.top().c,s.pop();
}
signed main()
{
	n=read();m=read();k=1;
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),c=read();
		e[i]=edge{u,v,c};
		bg[i]=1;ed[i]=-1;
		Mp[mp(u,v)]=i;
	}
	q=read();
	while(q--)
	{
		int op=read(),u=read(),v=read();
		if(op==1)
		{
			int c=read();
			e[++m]=edge{u,v,c};
			bg[m]=k;ed[m]=-1;
			Mp[mp(u,v)]=m;
		}
		if(op==2)
			ed[Mp[mp(u,v)]]=k-1;
		if(op==3)
		{
			qu[k]=u;qv[k]=v,k++;
		}
	}
	k--;
	for(int i=1;i<=m;i++)
		if(ed[i]==-1) ed[i]=k;
	for(int i=1;i<=m;i++)
		if(bg[i]<=ed[i]) ins(1,1,k,bg[i],ed[i],e[i]);
	cdq(1,1,k,a);
}

有一道差不多的题:八纵八横,只不过这个题要用 bitset \text{bitset} bitset,就贴个代码吧。

#include <cstdio>
#include <bitset>
#include <vector>
#include <iostream>
using namespace std;
#define bs bitset<1005>
const int M = 2005;
int read()
{	
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,k,q,tp,a[M],fa[M],dep[M],st[M];char s[10];
bs dis[M];
struct node
{
	int u,v;bs c;
}e[M];vector<node> v[2*M];
struct basis
{
	bs p[M];
	void ins(bs x)
	{
		for(int i=1000;i>=0;i--)
		{
			if(!x.test(i)) continue;
			if(p[i].none()) {p[i]=x;return ;}
			x^=p[i];
		}
	}
	bs ask()
	{
		bs t;
		for(int i=1000;i>=0;i--)
			if(!t[i])
				t^=p[i];
		return t;
	}
}emp;
void print(bs a)
{
	int f=0;
	for(int i=1000;i>=0;i--)
		if(a[i]==0 && f==0);
		else f=1,cout<<a[i];
	puts("");
}
void ins(int i,int l,int r,int L,int R,node x)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R)
	{
		v[i].push_back(x);
		return ;
	}
	int mid=(l+r)>>1;
	ins(i<<1,l,mid,L,R,x);
	ins(i<<1|1,mid+1,r,L,R,x);
}
int find(int x)
{
	return x==fa[x]?x:find(fa[x]);
}
bs get(int x)
{
	return x==fa[x]?0:dis[x]^get(fa[x]);
}
void back(int t)
{
	while(tp>t)
	{
		int y=st[tp--],x=st[tp--];
		if(y<0) dep[x]--,y-=y;
		fa[y]=y;dis[y]=0;
	}
}
void ask(int p,int l,int r,basis a)
{
	int cur=tp;
	for(int i=0;i<v[p].size();i++)
	{
		int x=v[p][i].u,y=v[p][i].v,fu=find(x),fv=find(y);
		bs c=v[p][i].c^get(x)^get(y);
		if(find(x)==find(y)) a.ins(c);
		else
		{
			if(dep[fu]<dep[fv]) swap(fu,fv);
			fa[fv]=fu;dis[fv]=c;st[++tp]=fu;st[++tp]=fv;
			if(dep[fu]==dep[fv]) dep[fu]++,st[tp]*=-1;
		}
	}
	if(l==r)
	{
		print(a.ask());
		back(cur);
		return ;
	}
	int mid=(l+r)>>1;
	ask(p<<1,l,mid,a);
	ask(p<<1|1,mid+1,r,a);
	back(cur);
}
int main()
{
	n=read();m=read();q=read();
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		e[i].u=read();e[i].v=read();cin>>e[i].c;
		ins(1,0,q,0,q,e[i]);
	}
	for(int i=1;i<=q;i++)
	{
		scanf("%s",s);
		if(s[0]=='A')
		{
			e[++k].u=read();e[k].v=read();cin>>e[k].c;
			a[k]=i;
		}
		if(s[0]=='C' && s[1]=='a')
		{
			int t=read();
			ins(1,0,q,a[t],i-1,e[t]);a[t]=0;
		}
		if(s[0]=='C' && s[1]=='h')
		{
			int t=read();
			ins(1,0,q,a[t],i-1,e[t]);a[t]=i;
			cin>>e[t].c;
		}
	}
	for(int i=1;i<=k;i++)
		if(a[i]) ins(1,0,q,a[i],q,e[i]);
	ask(1,0,q,emp);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值