[BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)
Posted HocRiser
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)相关的知识,希望对你有一定的参考价值。
4568: [Scoi2016]幸运数字
Time Limit: 60 Sec Memory Limit: 256 MB
Submit: 2131 Solved: 865
[Submit][Status][Discuss]Description
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。Input
第一行包含 2 个正整数 n ,q,分别表示城市的数量和旅行者数量。第二行包含 n 个非负整数,其中第 i 个整数 Gi 表示 i 号城市的幸运值。随后 n-1 行,每行包含两个正整数 x ,y,表示 x 号城市和 y 号城市之间有一条道路相连。随后 q 行,每行包含两个正整数 x ,y,表示这名旅行者的旅行计划是从 x 号城市到 y 号城市。N<=20000,Q<=200000,Gi<=2^60Output
输出需要包含 q 行,每行包含 1 个非负整数,表示这名旅行者可以保留的最大幸运值。
Sample Input
4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4Sample Output
14
11HINT
Source
[Submit][Status][Discuss]
线性基不支持删除,但是支持插入与合并,于是显然可以树剖维护,$O(n\log^4n)$。
线性基不支持修改,浪费了线段树支持修改的功能,实际上可以直接用不支持修改的ST表,$O(n\log^3n)$。
点分治不仅可以做路径统计问题,还可以处理与路径有关的询问问题,将每个询问的两个点的vector中放入这个询问,每次递归到一个重心时查找管辖范围内的所有询问,因为一个询问只涉及两个点,所以复杂度是有保证的。$O(n\log^2n)$
下面是倍增LCA的代码,要注意关于点的LCA和普通的是有区别的:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=20100,M=62; 9 ll g[N][16][M],Ans[M],a[N]; 10 int n,Q,u,v,cnt,to[N<<1],nxt[N<<1],h[N],d[N],fa[N][16]; 11 12 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 13 void ins(ll p[],ll x){ 14 for (int i=61; ~i; i--) if (x&(1ll<<i)){ 15 if (!p[i]) { p[i]=x; break; } else x^=p[i]; 16 } 17 } 18 void merge(ll g[],ll f1[],ll f2[]){ 19 rep(i,0,61) g[i]=f1[i]; 20 rep(i,0,61) if (f2[i]) ins(g,f2[i]); 21 } 22 void dfs(int x){ 23 ins(g[x][0],a[x]); 24 rep(i,1,15){ 25 fa[x][i]=fa[fa[x][i-1]][i-1]; 26 merge(g[x][i],g[x][i-1],g[fa[x][i-1]][i-1]); 27 } 28 for (int i=h[x],k; i; i=nxt[i]) 29 if ((k=to[i])!=fa[x][0]) fa[k][0]=x,d[k]=d[x]+1,dfs(k); 30 } 31 32 void lca(int u,int v){ 33 memset(Ans,0,sizeof(Ans)); 34 if (d[u]<d[v]) swap(u,v); 35 int t=d[u]-d[v]; 36 for (int i=15; ~i; i--) 37 if (t&(1<<i)) merge(Ans,Ans,g[u][i]),u=fa[u][i]; 38 if (u==v){ merge(Ans,Ans,g[u][0]); return; } 39 for (int i=15; ~i; i--) 40 if (fa[u][i]!=fa[v][i]) 41 merge(Ans,Ans,g[u][i]),merge(Ans,Ans,g[v][i]), 42 u=fa[u][i],v=fa[v][i]; 43 merge(Ans,Ans,g[u][0]); merge(Ans,Ans,g[v][0]); 44 merge(Ans,Ans,g[fa[u][0]][0]); 45 } 46 47 ll get(ll p[]){ 48 ll res=0; 49 for (int i=61; ~i; i--) res=max(res,res^p[i]); 50 return res; 51 } 52 53 int main(){ 54 freopen("bzoj4568.in","r",stdin); 55 freopen("bzoj45682.out","w",stdout); 56 scanf("%d%d",&n,&Q); 57 rep(i,1,n) scanf("%lld",&a[i]); 58 rep(i,2,n) scanf("%d%d",&u,&v),add(u,v),add(v,u); 59 dfs(1); 60 while (Q--) scanf("%d%d",&u,&v),lca(u,v),printf("%lld\n",get(Ans)); 61 return 0; 62 }
然后是点分治,本机时间跑的是倍增的一半,交到OJ上就莫名其妙的死活TLE,弃疗。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=l; i<=r; i++) 6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=20010,M=200010,inf=1000000000; 11 int n,Q,u,v,cnt,S,rt,tim,pos[N],f[N],b[N],sz[N],vis[N],d[N]; 12 int fa[N][16],h[N],to[N<<1],nxt[N<<1]; 13 ll a[N],g[N][65],Ans[65],ans[M]; 14 struct P{ int u,v; }s[M]; 15 vector<int>V[N]; 16 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 17 18 void ins(ll p[],ll x){ 19 for (int i=60; ~i; i--) if (x&(1ll<<i)){ 20 if (!p[i]) { p[i]=x; break; } else x^=p[i]; 21 } 22 } 23 24 void merge(ll g[],ll f1[],ll f2[]){ 25 rep(i,0,60) g[i]=f1[i]; 26 rep(i,0,60) if (f2[i]) ins(g,f2[i]); 27 } 28 29 ll get(ll p[]){ 30 ll res=0; 31 for (int i=60; ~i; i--) res=max(res,res^p[i]); 32 return res; 33 } 34 35 void find(int x,int fa){ 36 f[x]=0; sz[x]=1; 37 For(i,x) if ((k=to[i])!=fa && !vis[k]) 38 find(k,x),sz[x]+=sz[k],f[x]=max(f[x],sz[k]); 39 f[x]=max(f[x],S-sz[x]); 40 if (f[x]<f[rt]) rt=x; 41 } 42 43 void work(int x,int fa,int bel){ 44 pos[x]=bel; b[x]=tim; 45 rep(i,0,60) g[x][i]=g[fa][i]; ins(g[x],a[x]); 46 For(i,x) if ((k=to[i])!=fa && !vis[k]) work(k,x,bel); 47 } 48 49 void work1(int x,int fa){ 50 for (vector<int>::iterator it=V[x].begin(); it!=V[x].end(); it++){ 51 int k=*it; if (ans[k]) continue; 52 int u=s[k].u; if (u==x) u=s[k].v; 53 if (b[u]==tim && (pos[u]!=pos[x] || !pos[u])) 54 merge(Ans,g[u],g[x]),ans[k]=get(Ans); 55 } 56 For(i,x) if ((k=to[i])!=fa && !vis[k]) work1(k,x); 57 } 58 59 void solve(int x){ 60 vis[x]=1; b[x]=++tim; pos[x]=0; 61 rep(i,0,60) g[x][i]=0; ins(g[x],a[x]); 62 For(i,x) if (!vis[k=to[i]]) work(k,x,k); work1(x,0); 63 For(i,x) if (!vis[k=to[i]]) S=sz[k],f[rt=0]=inf,find(k,x),solve(k); 64 } 65 66 int main(){ 67 freopen("bzoj4568.in","r",stdin); 68 freopen("bzoj4568.out","w",stdout); 69 scanf("%d%d",&n,&Q); 70 rep(i,1,n) scanf("%lld",&a[i]); 71 rep(i,2,n) scanf("%d%d",&u,&v),add(u,v),add(v,u); 72 rep(i,1,Q) scanf("%d%d",&s[i].u,&s[i].v),V[s[i].u].push_back(i),V[s[i].v].push_back(i); 73 f[rt=0]=inf; S=n; find(1,0); solve(rt); 74 rep(i,1,Q) printf("%lld\n",ans[i]); 75 return 0; 76 }
以上是关于[BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 4568 4568: [Scoi2016]幸运数字 (线性基+树链剖分+线段树)
[BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)