UOI 2023 An Array and XOR 题解

题面:UOI 2023 An Array and XOR
题意简述:
给定长为 \(n\) 的序列 \(a\),值域在 \([0,2^m)\) 内,\(q\) 次询问 \(l,r\),定义 \(f(x)\)\(x\) 异或序列 \([l,r]\) 中的结果的最小值,询问 \(\max_{x \in [0,2^m-1]}f(x)\)

解题思路:
对二进制下每一位来观察,若区间内每个数第 \(j\) 位都相同,则令 \(x\)\(j\) 位取相反值可以使最终结果这一位为 \(1\),否则由于取最小异或值这一位为 \(0\)
想到建出 trie 树,区间内某一位的取值种类转化为树上节点的儿子数,令 \(dp_u\) 表示 \(u\) 子树内异或值的最大值,则可以得到转移方程,设当前位为 \(w\),当有两个儿子时,\(dp_u = \max(dp_{ls},dp_{rs})\),只有一个儿子时 \(dp_x=dp_s+2^w\)
既然有 \(q\) 次询问,支持离线,不难想到莫队,令 trie 树支持插入和删除,我们可以单次做到 \(O(m)\) 修改 \(dp\) 值,这样我们就做到了 \(O(nm \sqrt q )\) 的复杂度,这是无法通过的,代码如下。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m,k,t;
const int N = 1e5+10;
int a[N],ans[5*N];
struct qy{
    int l,r,id;
}q[5*N];
bool cmp(qy a,qy b){
    if(a.l/t!=b.l/t){
        return a.l/t<b.l/t;
    }
    if((a.l/t)&1)return a.r<b.r;
    return a.r>b.r;
}
struct trie{
    int tr[N*55][2],tot,cnt[N*55],dp[N*55];
    void ins(int x,int w,int v){
        cnt[x]++;dp[x]=0;
        if(w<0)return;
        int mk=(v>>w)&1ll;
        if(!tr[x][mk])tr[x][mk]=++tot;
        ins(tr[x][mk],w-1,v);
        bool fl=(tr[x][0]&&cnt[tr[x][0]]>0);
        bool fr=(tr[x][1]&&cnt[tr[x][1]]>0);
        if(fl&&fr)dp[x]=max(dp[tr[x][0]],dp[tr[x][1]]);
        else if(fl)dp[x]=dp[tr[x][0]]+(1ll<<w);
        else if(fr)dp[x]=dp[tr[x][1]]+(1ll<<w);
    }
    void del(int x,int w,int v){
        cnt[x]--;dp[x]=0;
        if(w<0)return;
        int mk=(v>>w)&1ll;
        del(tr[x][mk],w-1,v);
        bool fl=(tr[x][0]&&cnt[tr[x][0]]>0);
        bool fr=(tr[x][1]&&cnt[tr[x][1]]>0);
        if(fl&&fr)dp[x]=max(dp[tr[x][0]],dp[tr[x][1]]);
        else if(fl)dp[x]=dp[tr[x][0]]+(1ll<<w);
        else if(fr)dp[x]=dp[tr[x][1]]+(1ll<<w);
    }
}T;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>k>>m;
    t=sqrt(n);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=k;i++){
        cin>>q[i].l>>q[i].r;
        q[i].id=i;
    }
    sort(q+1,q+1+k,cmp);
    int ll=1,rr=0;
    for(int i=1;i<=k;i++){
        while(rr<q[i].r)T.ins(0,m-1,a[++rr]);
        while(ll>q[i].l)T.ins(0,m-1,a[--ll]);
        while(rr>q[i].r)T.del(0,m-1,a[rr--]);
        while(ll<q[i].l)T.del(0,m-1,a[ll++]);
        ans[q[i].id]=T.dp[0];
    }
    for(int i=1;i<=k;i++){
        cout<<ans[i]<<endl;
    }
    return 0;
}

既然要求某一位在区间内没有不同的值才能产生贡献,那么我们可以通过正反扫描一遍 trie 处理出每个数的每一位前面和后面最近的一个和他不一样的位置,分别记作 \(fl\)\(fr\)。那么对于 \(l<fl_{i,j}\)\(fr_{i,j}<r\) 的询问,\(j\) 这一位才可以贡献答案。
不妨对右端点从大到小做扫描线,每扫到一个 \(fr_{x}-1\),说明 \(x\) 可以开始贡献,为了保证正确性,按照 \(fl_{x,j}\) 从小到大来枚举 \(j\),令当前能贡献的值为 \(cur\)\(cur\) 依次或上 \(2^j\),再把 \(cur\) 加入到数据结构的 \(fl_{x,j}+1\) 这个位置上。
当扫到询问的右端点 \(r\) 时,向数据结构询问 \([1,l]\) 的最大值。
使用线段树或树状数组的话,单次询问和修改都是 \(O(\log n)\) 的,故而总时间复杂度为 \(O(nm^2\log n+q\log n)\),但是因为 \(nm^2\) 过大所以无法通过,代码如下。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
inline int read(){
    int k=0,f=1,c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())k=k*10+(c^48);
    return k*f;
}
inline void write(int x){
    if(x<10)putchar(x+'0');
    else write(x/10),putchar(x%10+'0');
}
int n,m,k;
const int N = 1e5+10;
int a[N],ans[5*N];
int fl[N][55],fr[N][55],p[N][55];
struct trie{
    int t[N*55][2],tot;
    vector<int>v[55*N];
    void init(){
        for(int i=0;i<=tot;i++){
            t[i][0]=t[i][1]=0;
            v[i].clear();
        }
        tot=0;
    }
    void ins(int x,int id,bool typ){
        int p=0;
        for(int w=m-1;w>=0;w--){
            int mk=(x>>w)&1ll;
            if(!t[p][mk])t[p][mk]=++tot;
            for(auto u:v[t[p][mk^1]]){
                if(typ)fr[u][w]=id;
                else fl[u][w]=id;
            }
            v[t[p][mk^1]].clear();
            v[t[p][mk]].push_back(id);
            p=t[p][mk];
        }
    }
}T;
int val[55];
bool cmp(int x,int y){
    return val[x]<val[y];
}
struct qy{
    int l,id;
};
vector<qy>q[N];
vector<int>s[N];
int lowbit(int x){
    return x&(-x);
}
struct bit{
    int t[N];
    void add(int p,int v){
        for(int i=p;i<=n;i+=lowbit(i)){
            t[i]=max(t[i],v);
        }
    }
    int query(int p){
        int res=0;
        for(int i=p;i;i-=lowbit(i)){
            res=max(res,t[i]);
        }
        return res;
    }
}t;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    n=read(),k=read(),m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    T.init();
    for(int i=1;i<=n;i++){
        T.ins(a[i],i,1);
    }
    T.init();
    for(int i=n;i>=1;i--){
        T.ins(a[i],i,0);
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<m;j++){
            p[i][j]=j;
            if(!fr[i][j])fr[i][j]=n+1;
            fl[i][j]++;fr[i][j]--;
            s[fr[i][j]].push_back(i);
            val[j]=fl[i][j];
        }
        sort(p[i],p[i]+m,cmp);
    }
    for(int i=1;i<=k;i++){
        int l,r;
        l=read(),r=read();
        q[r].push_back({l,i});
    }
    for(int i=n;i>=1;i--){
        for(auto v:s[i]){
            int cur=0;
            for(int j=0;j<m;j++){
                int x=p[v][j];
                if(fr[v][x]>=i){
                    cur|=(1ll<<x);
                }
                t.add(fl[v][x],cur);
            }
        }
        for(auto [l,id]:q[i]){
            ans[id]=t.query(l);
        }
    }
    for(int i=1;i<=k;i++){
        write(ans[i]);
        putchar(endl);
    }
    return 0;
}

然后我们为了平衡修改和查询的复杂度,考虑使用分块。分块维护最大值只要 \(O(1)\) 修改,\(O(\sqrt n)\) 查询,所以总时间复杂度 \(O(nm^2+q\sqrt n)\),可以通过。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
inline int read(){
    int k=0,f=1,c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())k=k*10+(c^48);
    return k*f;
}
inline void write(int x){
    if(x<10)putchar(x+'0');
    else write(x/10),putchar(x%10+'0');
}
int n,m,k,t;
const int N = 1e5+10;
int a[N],ans[5*N];
int fl[N][55],fr[N][55],p[N][55];
struct trie{
    int t[N*55][2],tot;
    vector<int>v[55*N];
    void init(){
        for(int i=0;i<=tot;i++){
            t[i][0]=t[i][1]=0;
            v[i].clear();
        }
        tot=0;
    }
    void ins(int x,int id,bool typ){
        int p=0;
        for(int w=m-1;w>=0;w--){
            int mk=(x>>w)&1ll;
            if(!t[p][mk])t[p][mk]=++tot;
            for(auto u:v[t[p][mk^1]]){
                if(typ)fr[u][w]=id;
                else fl[u][w]=id;
            }
            v[t[p][mk^1]].clear();
            v[t[p][mk]].push_back(id);
            p=t[p][mk];
        }
    }
}T;
int val[55];
bool cmp(int x,int y){
    return val[x]<val[y];
}
struct qy{
    int l,id;
};
vector<qy>q[N];
vector<int>s[N];
int L[N],R[N],pos[N],mx[N],kmx[N];
void change(int l,int v){
    mx[l]=max(mx[l],v);
    kmx[pos[l]]=max(kmx[pos[l]],v);
}
int query(int l,int r){
    int p=pos[l],q=pos[r];
    if(p==q){
        int ans=0;
        for(int i=l;i<=r;i++)ans=max(ans,mx[i]);
        return ans;
    }
    int ans=0;
    for(int i=l;i<=R[p];i++)ans=max(ans,mx[i]);
    for(int i=p+1;i<q;i++)ans=max(ans,kmx[i]);
    for(int i=L[q];i<=r;i++)ans=max(ans,mx[i]);
    return ans;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    n=read(),k=read(),m=read();
    t=sqrt(n);
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if(R[t]<n)t++,L[t]=R[t-1]+1,R[t]=n;
    for(int i=1;i<=t;i++){
        for(int j=L[i];j<=R[i];j++)pos[j]=i;
    }
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    T.init();
    for(int i=1;i<=n;i++){
        T.ins(a[i],i,1);
    }
    T.init();
    for(int i=n;i>=1;i--){
        T.ins(a[i],i,0);
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<m;j++){
            p[i][j]=j;
            if(!fr[i][j])fr[i][j]=n+1;
            fl[i][j]++;fr[i][j]--;
            s[fr[i][j]].push_back(i);
            val[j]=fl[i][j];
        }
        sort(p[i],p[i]+m,cmp);
    }
    for(int i=1;i<=k;i++){
        int l,r;
        l=read(),r=read();
        q[r].push_back({l,i});
    }
    for(int i=n;i>=1;i--){//扫描r
        for(auto v:s[i]){
            int cur=0;
            for(int j=0;j<m;j++){
                int x=p[v][j];
                if(fr[v][x]>=i){
                    cur|=(1ll<<x);
                }
                change(fl[v][x],cur);
            }
        }
        for(auto [l,id]:q[i]){
            ans[id]=query(1,l);
        }
    }
    for(int i=1;i<=k;i++){
        write(ans[i]);
        putchar(endl);
    }
    return 0;
}
posted @ 2026-08-08 11:25  farenara_sayowell  阅读(8)  评论(0)    收藏  举报