码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-08-15 06:03:29      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:color   多个   href   linear   题意   scan   数据   merge   线性   

传送: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]幸运数字(线性基+树上倍增)

标签:color   多个   href   linear   题意   scan   数据   merge   线性   

原文地址:https://www.cnblogs.com/changer-qyz/p/11355642.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!