zoukankan      html  css  js  c++  java
  • 《洛谷P3292 [SCOI2016]幸运数字》

    这道题真的让我受益匪浅,加深了对线性基的认识。

    首先,考虑了一般解法,较暴力。

    将x->y路径上所有的点都加入一个空的线性基集合,最后求一次最大值即可。

    既然要将每个点都加入,那么必然要一个点一个点地跳。(因为每个点都要遍历到,所以不能倍增)

    那么,这时最坏复杂度在N^2。只有30分。

    考虑去优化。

    思路1:首先,对于线性基,它是可以合并的,即将线性基A里的每个数都插入到线性基B中。

    所以我们只需要做到的就是高效地合并,高效地查询。

    因为是树上的合并,树剖登场。

    对于线段树上的点,我们去维护它的子节点合并后的线性基。

    那么我们树剖查询时,将一整条链的线性基都合并到答案中。

    每次都能logn处理出一条链的点。那么复杂度就降到了nlogn^4左右。

    然后就可以过了。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 2e4+5;
    const int M = 1e4;
    const LL Mod = 1e9+7;
    #define rg register
    #define pi acos(-1)
    #define INF 1e18
    #define CT0 cin.tie(0),cout.tie(0)
    #define IO ios::sync_with_stdio(false)
    #define dbg(ax) cout << "now this num is " << ax << endl;
    namespace FASTIO{
        inline LL read(){
            LL 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;
        }
        void print(int x){
            if(x < 0){x = -x;putchar('-');}
            if(x > 9) print(x/10);
            putchar(x%10+'0');
        }
    }
    using namespace FASTIO;
    void FRE(){
    /*freopen("data1.in","r",stdin);
    freopen("data1.out","w",stdout);*/}
    
    int n,q,tim = 0,ssize[N],son[N],id[N],rk[N],top[N],fa[N],dep[N];
    LL val[N],p[N];
    vector<int> G[N];
    struct Node{
        int L,r;
        LL p[61];
    }node[N<<2];
    void dfs(int u,int ffa)
    {
        ssize[u] = 1,dep[u] = dep[ffa]+1,fa[u]= ffa;
        for(auto v : G[u])
        {
            if(v == ffa) continue;
            dfs(v,u);
            ssize[u] += ssize[v];
            if(ssize[v] > ssize[son[u]]) son[u] = v;
        }
    }
    void dfs1(int u,int sta)
    {
        top[u] = sta;
        id[u] = ++tim;
        rk[tim] = u;
        if(!son[u]) return ;
        dfs1(son[u],sta);
        for(auto v : G[u]) if(v != fa[u] && v != son[u]) dfs1(v,v);
    }
    void insert(LL p[],LL x)
    {
        for(rg int i = 60;i >= 0;--i)
        {
            if(((x>>i)&1) == 0) continue;
            if(p[i] == 0){p[i] = x;return ;}
            else x ^= p[i];
        }
    }
    LL Get_Max()
    {
        LL ma = 0;
        for(rg int i = 60;i >= 0;--i) 
        {
            //if(p[i]) printf("p[%d] is %lld
    ",i,p[i]);
            ma = max(ma,ma^p[i]);
        }
        return ma;
    }
    void Merge(LL p1[],LL p2[])
    {
        for(rg int i = 60;i >= 0;--i)
        {
            if(p2[i]) insert(p1,p2[i]);
        }
    }
    void Pushup(int idx)
    {
        Merge(node[idx].p,node[idx<<1].p);
        Merge(node[idx].p,node[idx<<1|1].p);
    }
    void build(int L,int r,int idx)
    {
        node[idx].L = L,node[idx].r = r;
        if(L == r)
        {
            insert(node[idx].p,val[rk[L]]);
            return ;
        }
        int mid = (L+r)>>1;
        build(L,mid,idx<<1);
        build(mid+1,r,idx<<1|1);
        Pushup(idx);
    }
    void query(int L,int r,int idx)
    {
        if(node[idx].L >= L && node[idx].r <= r)
        {
            Merge(p,node[idx].p);
            return ;
        }
        int mid = (node[idx].L+node[idx].r)>>1;
        if(mid >= L) query(L,r,idx<<1);
        if(mid < r) query(L,r,idx<<1|1);
    }
    void tree_query(int x,int y)
    {
        while(top[x] != top[y])
        {
            if(dep[top[x]] < dep[top[y]]) swap(x,y);
            query(id[top[x]],id[x],1);
            x = fa[top[x]];
        }
        if(dep[x] > dep[y]) swap(x,y);
        query(id[x],id[y],1);
    }
    int main()
    {
        n = read(),q = read();
        for(rg int i = 1;i <= n;++i) val[i] = read();
        for(rg int i = 1;i < n;++i)
        {
            int u,v;u = read(),v = read();
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1,0);dfs1(1,0);build(1,n,1);
        while(q--)
        {
            int x,y;x = read(),y = read();
            memset(p,0,sizeof(p));
            tree_query(x,y);
            LL ans = Get_Max();
            printf("%lld
    ",ans);
        }
        system("pause");
    }
    
    
    /*
    6 10
    1 3 11 5 7 9
    1 2
    2 4
    2 5
    1 3
    3 6
    */
    View Code

    思路2:我们可以发现,对于我们想要查询的点,我们必定要保证它在x->y的路径上才行。

    那么我们用p[i][j]来表示1->i的线性基。然后从父节点往下转移即可。然后我们保证j位置上的线性基的深度尽量大,这样就能使线性基尽量在链上

    每次都插入i点去更新线性基。

    注意的是,这里更新线性基,不引入参数数组,就会很难写。

    对于j位置如果当前的点深度更大,那么j位置的线性基显然要更新为当前点的价值。

    那么当前点的价值显然不能再去更新低点(就像一开始没有值一样)

    此时我们的值就应该变为原来的j位置的线性基(因为它现在被挤下来了),所以用它去更新。(这里也是最麻烦的地方)

    然后就对于x,y都进行一次线性基的更新即可。

    // Author: levil
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 2e4+5;
    const int M = 1e4;
    const LL Mod = 1e9+7;
    #define rg register
    #define pi acos(-1)
    #define INF 1e18
    #define CT0 cin.tie(0),cout.tie(0)
    #define IO ios::sync_with_stdio(false)
    #define dbg(ax) cout << "now this num is " << ax << endl;
    namespace FASTIO{
        inline LL read(){  
            LL 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;
        }
        void print(int x){
            if(x < 0){x = -x;putchar('-');}
            if(x > 9) print(x/10);
            putchar(x%10+'0');
        }
    }
    using namespace FASTIO;
    void FRE(){
    /*freopen("data1.in","r",stdin);
    freopen("data1.out","w",stdout);*/}
    
    LL v[N],p[N][65];//1~i位置对应的线性基。
    vector<int> G[N];
    int dep[N],lg[N],f[N][30],pos[N][65];//pos记录线性基的位置,确保深度最大
    void init()
    {
        lg[0] = 1;
        for(rg int i = 1;i < N;++i) lg[i] = lg[i-1] + ((1<<lg[i-1]) == i);
    }
    void update(int u,LL p[],int pos[])
    {
        LL x = v[u];
        for(rg int i = 61;i >= 0;--i)
        {
            if(((x>>i)&1) == 0) continue;
             if(!p[i])
            {
                 p[i] = x;pos[i] = u;
                return ;
            }
            if(dep[u] > dep[pos[i]])
            {
               swap(pos[i],u);
               swap(x,p[i]);
            }
            x ^= p[i];
        }
    }
    void dfs(int u,int fa)
    {
        dep[u] = dep[fa]+1,f[u][0] = fa;
        for(rg int i = 1;i <= lg[dep[u]];++i) f[u][i] = f[f[u][i-1]][i-1];
    
        for(rg int i = 61;i >= 0;--i) p[u][i] = p[fa][i],pos[u][i] = pos[fa][i];
        update(u,p[u],pos[u]);
    
        for(auto v : G[u]) if(v != fa) dfs(v,u);
    }
    int LCA(int x,int y)
    {
        if(dep[x] < dep[y]) swap(x,y);
        while(dep[x] > dep[y]) x = f[x][lg[dep[x]-dep[y]]-1];
        if(x == y) return x;
        for(rg int i = lg[dep[x]]-1;i >= 0;--i) if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i];
        return f[x][0];
    }
    //CF1100F Ivan and Burgers
    int main()
    {
        init();
        int n,q;n = read(),q = read();
        for(rg int i = 1;i <= n;++i) v[i] = read();
        for(rg int i = 1;i < n;++i)
        {
            int x,y;x = read(),y = read();
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dfs(1,0);
        while(q--)
        {
            int x,y;x = read(),y = read();
            int lca = LCA(x,y);
            LL f[65];
            for(rg int i = 61;i >= 0;--i) 
            {
                if(dep[pos[x][i]] >= dep[lca]) f[i] = p[x][i];
                else f[i] = 0;
            }
            for(rg int i = 61;i >= 0;--i)
            {
                if(dep[pos[y][i]] >= dep[lca])
                {
                    LL val = p[y][i];
                    for(rg int j = i;j >= 0;--j)
                    {
                        if(((val>>j)&1) == 0) continue;
                        if(f[j] == 0){f[j] = val;break;}
                        val ^= f[j];
                    }
                }
            }
            LL ans = 0;
            for(rg int i = 61;i >= 0;--i) 
            {
                ans = max(ans,ans^f[i]);
            }
            printf("%lld
    ",ans);
        }
        system("pause");
    }
    
    /*
    6 10
    1 3 11 5 7 9
    1 2
    2 4
    2 5
    1 3
    3 6
    */
    View Code

     总结:两种方法相对来说树剖虽然代码多了点,但是非常容易理解。

    过了下思路,自己就写出来了。。(树剖日常忘rk放上线段树。。)

    第二种方法相对还是要比树剖快点。大概两个log吧。。

    其实还有第三种思路。

    那就是从点分治。。(orz)

  • 相关阅读:
    (CS模式)大学计算机基础考试系统
    四叶草的祝福
    做人的小故事!
    前天晚上回到北京了
    人生活的三种状态
    松口气了!
    Mysql一些基础用法
    云计算随想
    对vector与deque插值与遍历的性能数据
    gdb命令的常用调试选项
  • 原文地址:https://www.cnblogs.com/zwjzwj/p/13566220.html
Copyright © 2011-2022 走看看