牛客20308,洛谷3292 [SCOI2016]幸運數字

鏈接

https://www.luogu.org/problemnew/show/P3292
https://ac.nowcoder.com/acm/problem/20308

做題的經過(可以跳過)

這不是樹上倍增維護線性基的裸題嗎
然而我寫了一整天
故事是這樣的:
我先寫了個自認爲qlog3qlog^3的樹剖+線段樹維護區間線性基,然後TT了,事後發現原來這個算法的複雜度是q×(602)×log2nq\times (60^2) \times log^2n(這不妥妥TT掉嗎)
然後我又寫了個真的是qlog3qlog^3的倍增,誰知道洛谷機器慢的要死,還是TT
然後我又寫了個qlog2qlog^2的倍增,洛谷上還是TLETLE,我就開始懷疑人生,結果開了O2O2就過了,還挺快…

題解

就是個裸題,樹上倍增維護線性基
查詢的時候,最直接的做法就是一邊往上跳一邊查詢,這樣複雜度是O(q log2n log2260)O(q\ log_2n\ log_22^{60})
優化可以效仿序列上的RMQRMQ,先求lcalca,這樣我就知道了我要跳的區間的長度,這樣求出log2(log_2(區間長度)),就可以把原來的O(log2n)O(log_2n)次合併線性基優化到合併O(1)O(1)

代碼

#include <bits/stdc++.h>
#define maxn 20010
#define maxk 15
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
ll g[maxn], N, Q;
struct Graph
{
    int etot, head[maxn], to[maxn<<1], next[maxn<<1], w[maxn<<1], N;
    void clear()
    {
        int i;
        for(i=1;i<=N;i++)head[i]=0;
        for(i=1;i<=etot;i++)to[i]=next[i]=w[i]=0;
        N=etot=0;
    }
    void adde(int a, int b, int c){to[++etot]=b;w[etot]=c;next[etot]=head[a];head[a]=etot;}
}G;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
struct LinearBasis
{
    ll b[70];
    void clear(){cl(b);}
    void insert(ll t)
    {
        for(ll k(59);~k;k--)
            if(t&(1ll<<k))
            {
                if(!b[k])b[k]=t;
                t^=b[k];
            }
    }
    void insert(ll *a, ll len)
    {
        for(auto i(0);i<len;i++)insert(a[i]);
    }
    bool check(ll x)
    {
        for(ll k(59);~k;k--)
            if(x&(1ll<<k))x^=b[k];
        return !x;
    }
};
struct Doubling_LCA
{
    int f[maxn][maxk+2], depth[maxn];
    LinearBasis lb[maxn][maxk+2];
    void clear(){cl(f), cl(lb), cl(depth);}
    void dfs(Graph &G, int pos, int pre)
    {
        for(auto k=1;(1<<k)<=depth[pos];k++)f[pos][k]=f[f[pos][k-1]][k-1];
        for(auto k=1;(1<<k)<=depth[pos];k++)
        {
            lb[pos][k]=lb[pos][k-1];
            lb[pos][k].insert(lb[f[pos][k-1]][k-1].b,63);
        }
        for(auto p(G.head[pos]);p;p=G.next[p])
            if(G.to[p]!=pre)
            {
                f[G.to[p]][0]=pos;
                lb[G.to[p]][0].insert(g[G.to[p]]);
                depth[G.to[p]]=depth[pos]+1;
                dfs(G,G.to[p],pos);
            }
    }
    void run(Graph &G, int root)
    {
        depth[root]=1;
        lb[root][0].insert(g[root]);
        dfs(G,root,0);
    }
    int q(int x, int y)
    {
        if(depth[x]<depth[y])swap(x,y);
        for(auto k(maxk);~k;k--)
            if(depth[f[x][k]]>=depth[y])
                x=f[x][k];
        if(x==y)return x;
        for(auto k(maxk);~k;k--)
            if(f[x][k]!=f[y][k])
                x=f[x][k], y=f[y][k];
        return f[x][0];
    }
}dlca;
ll q(int x, int y)
{
    int lca=dlca.q(x,y), K, p, d;
    ll ans(0);
    LinearBasis lb; lb.clear();
    d=dlca.depth[x]-dlca.depth[lca]+1;
    K=log2(d);
    lb.insert(dlca.lb[x][K].b,59);
    for(auto k(maxk);~k;k--)if( d-(1<<K) & (1<<k) )x=dlca.f[x][k];
    lb.insert(dlca.lb[x][K].b,59);

    d=dlca.depth[y]-dlca.depth[lca]+1;
    K=log2(d);
    lb.insert(dlca.lb[y][K].b,59);
    for(auto k(maxk);~k;k--)if( d-(1<<K) & (1<<k) )y=dlca.f[y][k];
    lb.insert(dlca.lb[y][K].b,59);

    for(auto k(59);~k;k--)if( (ans&(1ll<<k))==0 )ans^=lb.b[k];

    return ans;
}
void init()
{
    ll i, u ,v;
    N=read(); Q=read();
    for(i=1;i<=N;i++)g[i]=read();
    for(i=1;i<N;i++)u=read(), v=read(), G.adde(u,v,0), G.adde(v,u,0);
    dlca.clear();
    dlca.run(G,1);
}
int main()
{
    init();
    while(Q--)
    {
        auto x=read(), y=read();
        printf("%lld\n",q(x,y));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章