BZOJ4568 [Scoi2016]幸运数字 【点分治 + 线性基】

题目链接

BZOJ4568

题解

选任意个数异或和最大,使用线性基

线性基插入\(O(logn)\),合并\(O(log^2n)\)

我们要求树上两点间异或和最大值,由于合并是\(O(log^2n)\)的,我们尽量只合并一次
那就采用点分治
每次求出到分治重心的线性基,将过分治重心的询问的两个线性基合并即可
复杂度\(O(60^2q + 60nlogn)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 20005,maxm = 200005,INF = 1000000000;
inline LL read(){
    LL out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
struct Bit{
    LL A[60];
    void init(){for (int i = 59; ~i; i--) A[i] = 0;}
    void copy(LL* B){for (int i = 59; ~i; i--) A[i] = B[i];}
    void ins(LL x){
        for (int i = 59; ~i; i--)
            if ((x >> i) & 1){
                if (A[i]) x ^= A[i];
                else {A[i] = x; break;}
            }
    }
    LL ask(){
        LL re = 0;
        for (int i = 59; ~i; i--) if ((re ^ A[i]) > re) re ^= A[i];
        return re;
    }
}B[maxn],T;
int h[maxn],ne = 1;
struct EDGE{int to,nxt;}ed[maxn << 1];
inline void build(int u,int v){
    ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
    ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
}
int g[maxn],nxt[30 * maxm],tq[30 * maxn],cnt;
int n,q,x[maxm],y[maxm],vis[maxn],pos[maxn],Vis[maxn],now;
LL G[maxn],ans[maxm];
void Add(int u,int v){
    nxt[++cnt] = g[u]; tq[cnt] = v; g[u] = cnt;
}
int F[maxn],siz[maxn],fa[maxn],N,rt;
void getrt(int u){
    F[u] = 0; siz[u] = 1;
    Redge(u) if (!vis[to = ed[k].to] && to != fa[u]){
        fa[to] = u; getrt(to);
        siz[u] += siz[to];
        F[u] = max(F[u],siz[to]);
    }
    F[u] = max(F[u],N - siz[u]);
    if (F[u] < F[rt]) rt = u;
}
void dfs(int u,int R){
    pos[u] = R; siz[u] = 1; Vis[u] = now;
    B[u].copy(B[fa[u]].A); B[u].ins(G[u]);
    Redge(u) if (!vis[to = ed[k].to] && to != fa[u]){
        fa[to] = u; dfs(to,R);
        siz[u] += siz[to];
    }
}
void solve(int u){
    F[rt = 0] = INF; N = siz[u]; getrt(u);
    //printf("u%d  rt%d\n",u,rt);
    pos[rt] = rt; vis[rt] = true; siz[u] = 1; Vis[rt] = ++now;
    B[rt].init(); B[rt].ins(G[rt]);
    Redge(rt) if (!vis[to = ed[k].to]){
        fa[to] = rt; dfs(to,to);
        siz[u] += siz[to];
    }
    for (int k = g[u],i,a,b; k; k = nxt[k]){
        i = tq[k]; a = x[i]; b = y[i];
        if (Vis[a] != now || Vis[b] != now) continue;
        if (pos[a] == pos[b]) Add(pos[a],i);
        else {
            T.copy(B[a].A);
            for (int j = 59; ~j; j--)
                if (B[b].A[j]) T.ins(B[b].A[j]);
            ans[i] = T.ask();
        }
    }
    Redge(rt) if (!vis[to = ed[k].to]){
        solve(to);
    }
}
int main(){
    n = read(); q = read();
    for (int i = 1; i <= n; i++) G[i] = read();
    for (int i = 1; i < n; i++) build(read(),read());
    for (int i = 1; i <= q; i++){
        x[i] = read(); y[i] = read();
        if (x[i] == y[i]) ans[i] = G[x[i]];
        else Add(1,i);
    }
    siz[1] = n; solve(1);
    for (int i = 1; i <= q; i++)
        printf("%lld\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/Mychael/p/9059228.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值