#41 最短路(分治+线性基)
Posted gloid
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#41 最短路(分治+线性基)相关的知识,希望对你有一定的参考价值。
考虑异或最短路应该怎么求。那么这是个WC原题,dfs一遍找到所有有用的环丢进线性基即可,因为每一个环的权值都是可以取到且不对其他部分产生影响的。
现在给了一棵树,不妨就把他看做原图的dfs树。每增加一条边就是增加了一个环。算出权值后,现在问题变为求一个数和任选一段区间里的数的最大异或值。
比较暴力的做法是直接建线段树,每次logn*log2v取出区间线性基。这样可以拿50分。
线性基的合并实在太慢了。考虑能不能离线搞。每次取出跨过区间中点的询问,处理中点左右的后缀前缀线性基,询问时将两边线性基合并。于是就变成logv(logn+logv)了。
调了半天发现m和n搞反了。
果然是NOIp模拟。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<cassert> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();} while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 300010 int n,m,q,p[N],deep[N],value[N],ans[N],t=0; struct base { int size,a[31]; void ins(int x) { for (register int i=30;~i;i--) { if (!x) break; if (x&(1<<i)) if (!a[i]) {a[i]=x;size++;break;} else x^=a[i]; } } base operator +(const base&b) const { base c; if (size>b.size) { c.size=size;for (int i=0;i<31;i++) c.a[i]=a[i]; if (size==31) return c; for (register int i=30;~i;i--) c.ins(b.a[i]); } else { c.size=b.size;for (int i=0;i<31;i++) c.a[i]=b.a[i]; if (b.size==31) return c; for (register int i=30;~i;i--) c.ins(a[i]); } return c; } }pre[N],suf[N]; struct data{int to,nxt,len; }edge[N<<1]; struct data2{int i,l,r,x; }Q[N],u[N]; void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;} void dfs(int k,int from) { for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) { deep[edge[i].to]=deep[k]^edge[i].len; dfs(edge[i].to,k); } } int work(int x,base p) { for (int i=30;~i;i--) x=min(x,x^p.a[i]); return x; } void solve(int l,int r,int low,int high) { if (l>r||low>high) return; if (low==high) { for (int i=l;i<=r;i++) ans[Q[i].i]=min(Q[i].x,Q[i].x^value[low]); return; } int mid=low+high>>1; for (int i=mid;i>=low;i--) { if (i==mid) pre[i]=(base){0,{0}}; else pre[i]=pre[i+1]; pre[i].ins(value[i]); } for (int i=mid+1;i<=high;i++) { if (i==mid+1) suf[i]=(base){0,{0}}; else suf[i]=suf[i-1]; suf[i].ins(value[i]); } int head=l-1,tail=r+1; for (int i=l;i<=r;i++) if (Q[i].l<=mid&&Q[i].r>mid) ans[Q[i].i]=work(Q[i].x,pre[Q[i].l]+suf[Q[i].r]); else if (Q[i].r<=mid) u[++head]=Q[i]; else u[--tail]=Q[i]; for (int i=l;i<=r;i++) Q[i]=u[i]; solve(l,head,low,mid); solve(tail,r,mid+1,high); } int main() { #ifndef ONLINE_JUDGE freopen("c.in","r",stdin); freopen("c.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif n=read(),m=read(),q=read(); for (int i=1;i<n;i++) { int x=read(),y=read(),z=read(); addedge(x,y,z),addedge(y,x,z); } dfs(1,1); for (int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); value[i]=deep[x]^deep[y]^z; } for (int i=1;i<=q;i++) { int s=read(),t=read(),l=read(),r=read(); Q[i].i=i,Q[i].x=deep[s]^deep[t],Q[i].l=l,Q[i].r=r; } solve(1,q,1,m); for (int i=1;i<=q;i++) printf("%d ",ans[i]); return 0; }
以上是关于#41 最短路(分治+线性基)的主要内容,如果未能解决你的问题,请参考以下文章
题解 CF938G Shortest Path Queries
loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)