luoguP3292 [SCOI2016]幸运数字(线性基+树上倍增)

Posted changer-qyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luoguP3292 [SCOI2016]幸运数字(线性基+树上倍增)相关的知识,希望对你有一定的参考价值。

传送:https://www.luogu.org/problem/P3292

题意:

$n$座城市,$n-1$条路,每个城市有一个价值$a_i$。$q$个询问,每次询问城市$x$到城市$y$的路径上经过的城市的价值的最大异或和为多少。

数据范围:

$1<=n<=20000,q<=200000,a_i<=2^60$。

分析:

对于一次询问$x-->y$的路径上的答案,很明显就是$x-->lca(x,y)$的线性基并上$y-->lca(x,y)$的线性基,然后求最大异或和。

那么对于多个询问来说怎么考虑呢?

在一棵树上查询$lca$有两种做法:1)tarjan/dfs(离线);2)ST表+倍增(在线)。对于这个题很明显需要维护路径上的线性基,我们用第二种倍增的做法在求$lca$的同时,就可以维护线性基,然后查询答案。

用$f[i][j]$代表点$i$向上跳$2^j$布达到哪一个点。用$LB[i][j]$代表向上$2^j$步路径上的线性基。

然后求$lca$的过程中同时暴力合并线性基可以了鸭。(qaaaaq

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=2e4+10;
 5 struct node
 6     int to,nxt;
 7 edge[maxn*2];
 8 int head[maxn],f[maxn][20],deep[maxn],tot,N,n,q;
 9 ll a[maxn];
10 struct Linear_basis
11     ll b[65];
12     bool insert(ll x)
13         for (int i=62;i>=0;i--)
14             if (x&(1ll<<i))
15                 if (!b[i])b[i]=x; break; 
16                 x^=b[i];
17              
18         
19         return x>0;  //是否可插入 true:可插入,false:不可插入 
20     
21     ll mx()
22         ll res=0;
23         for (int i=62;i>=0;i--) res=max(res,res^b[i]);
24         return res;
25     
26     void clear()
27         memset(b,0,sizeof(b));
28     
29 LB[maxn][20],ans;
30 void add(int x,int y)
31     edge[++tot]=y,head[x]; head[x]=tot;
32 
33 void dfs(int x,int fa)
34     f[x][0]=fa; //倍增数组 
35     LB[x][0].insert(a[x]); 
36     for (int i=head[x];i!=-1;i=edge[i].nxt)
37         node tmp=edge[i];
38         if (tmp.to==fa) continue;
39         deep[tmp.to]=deep[x]+1;
40         dfs(tmp.to,x);
41     
42 
43 Linear_basis merge(Linear_basis p,Linear_basis q)
44     Linear_basis tmp=p;
45     for (int i=62;i>=0;i--)
46         if (q.b[i]) tmp.insert(q.b[i]);
47     
48     return tmp;
49 
50 void init()
51     N=floor(log(1.0*n)/log(2.0));
52     deep[1]=0;
53     dfs(1,0);
54     for (int j=1;j<=N;j++)
55         for (int i=1;i<=n;i++)
56             f[i][j]=f[f[i][j-1]][j-1];
57             LB[i][j]=merge(LB[i][j-1],LB[f[i][j-1]][j-1]); 
58         
59     
60 
61 int _LCA(int x,int y)
62     if (deep[x]<deep[y]) swap(x,y);
63     for (int j=N;j>=0;j--)
64         if (deep[f[x][j]]>=deep[y])
65             ans=merge(ans,LB[x][j]);
66             x=f[x][j];
67         
68     if (x==y)
69         ans=merge(ans,LB[x][0]); return x;
70     
71     for (int j=N;j>=0;j--)
72         if (f[x][j]!=f[y][j])
73             ans=merge(ans,merge(LB[x][j],LB[y][j]));
74             x=f[x][j]; y=f[y][j];
75         
76     
77     Linear_basis tmp=merge(LB[x][0],LB[y][0]);
78     Linear_basis tmp2=merge(ans,LB[f[x][0]][0]);
79     ans=merge(tmp2,tmp);
80     return f[x][0]; //lca
81 
82 int main()
83     scanf("%d%d",&n,&q);
84     for (int i=1;i<=n;i++) scanf("%lld",&a[i]),head[i]=-1;
85     int x,y; tot=0;
86     for (int i=1;i<=n-1;i++)
87         scanf("%d%d",&x,&y);
88         add(x,y);add(y,x);
89     
90     init();
91     while (q--)
92         scanf("%d%d",&x,&y);
93         ans.clear(); 
94         _LCA(x,y);
95         printf("%lld\n",ans.mx()); 
96     
97     return 0;
98 

 

以上是关于luoguP3292 [SCOI2016]幸运数字(线性基+树上倍增)的主要内容,如果未能解决你的问题,请参考以下文章

P3292 [SCOI2016]幸运数字 [线性基+倍增]

Luogu P3292 [SCOI2016]幸运数字

BZOJ4568:[SCOI2016]幸运数字——题解

LibreOJ #2013. 「SCOI2016」幸运数字

bzoj 4568: [Scoi2016]幸运数字

BZOJ4568: [Scoi2016]幸运数字