【题解】Luogu P5283 [十二省联考2019]异或粽子

原题传送门

看见一段的异或和不难想到要做异或前缀和\(s\)

我们便将问题转化成:给定\(n\)个数,求异或值最靠前的\(k\)对之和

我们珂以建一个可持久化01trie,这样我们就珂以求出每个值\(s[a]\)与之前所有的值异或值最大的值\(b\)是多少,把这些所有\((b,a)\)塞进一个堆中

每次从堆顶取元素,设这个元素为\((b,a)\),要将\(b\)加入答案,并且在版本\(a\)的01trie中减去\(s[a]\)^\(b\),再取出\(s[a]\)与01trie中的数异或最大值(原来的次大值)\(c\),把\((c,a)\)塞进这个堆中

重复做k次即可得到答案

#include <bits/stdc++.h>
#define N 500005
#define uint unsigned int
#define ll long long
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline uint read()
{
    register uint x=0;register char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x;
}
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
int n,k;
ll ans=0;
uint a[N],b[N];
priority_queue< pair<ll,ll> > q;
struct node{
    int sum,ch[2];
}tr[N*60];
int root[N],tot;
inline void update(register int &x,register int pre,register uint val,register int dep)
{
    x=++tot;
    tr[x]=tr[pre];
    ++tr[x].sum;
    if(dep==-1)
        return;
    if((1ll<<dep)&val)
        update(tr[x].ch[1],tr[pre].ch[1],val,dep-1);
    else
        update(tr[x].ch[0],tr[pre].ch[0],val,dep-1);
}
inline uint query(register int x,register uint val,register int dep)
{
    if(dep==-1)
        return 0;
    if(val&(1ll<<dep))
    {
        if(tr[tr[x].ch[0]].sum)
            return query(tr[x].ch[0],val,dep-1);
        else
            return query(tr[x].ch[1],val,dep-1)+((uint)1<<dep);
    }
    else
    {
        if(tr[tr[x].ch[1]].sum)
            return query(tr[x].ch[1],val,dep-1)+((uint)1<<dep);
        else
            return query(tr[x].ch[0],val,dep-1);
    }
}
inline void modify(register int &x,register int pre,register uint val,register int dep)
{
    x=++tot;
    tr[x]=tr[pre];
    --tr[x].sum;
    if(dep==-1)
        return;
    if((1ll<<dep)&val)
        modify(tr[x].ch[1],tr[pre].ch[1],val,dep-1);
    else
        modify(tr[x].ch[0],tr[pre].ch[0],val,dep-1);
}
int main()
{
    n=read(),k=read();
    b[0]=0;
    for(register int i=1;i<=n;++i)
    {
        a[i]=read();
        b[i]=b[i-1]^a[i];
    }
    for(register int i=1;i<=n;++i)
        update(root[i],root[i-1],b[i-1],31);
    for(register int i=1;i<=n;++i)
    {
        uint tmp=query(root[i],b[i],31);
        q.push(make_pair(tmp^b[i],i));
    }
    while(k--&&!q.empty())
    {
        pair<ll,ll> tmp=q.top();
        q.pop();
        ans+=tmp.first;
        int pos=tmp.second;
        modify(root[pos],root[pos],tmp.first^b[pos],31);
        if(!tr[root[pos]].sum)
            continue;
        uint tmpp=query(root[pos],b[pos],31);
        q.push(make_pair(b[pos]^tmpp,pos));
    }
    write(ans);
    return 0;
}

原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/10957802.html

时间: 2024-10-09 16:53:59

【题解】Luogu P5283 [十二省联考2019]异或粽子的相关文章

P5283 [十二省联考2019]异或粽子

传送门 超级钢琴+可持久化$Trie$ 同样设三元组 $(o,l,r)$ 表示左端点为 $o$,右端点 $\in [l,r]$ 的区间的最大异或值,这个东西可以用可持久化 $Trie$ 来维护 一开始把所有 $(i,i,n)$ 扔到堆里,然后每次取出计算贡献,设取得最大异或值的位置为 $t$,然后再把 $(o,l,t-1)$ 和 $(o,t+1,r)$ 扔到堆里 具体还是看代码,很容易理解 注意可能爆 $int$,所以要开 $unsigned\ int$,要注意代码常数,我代码 $luogu$

P5283 [十二省联考2019]异或粽子 可持久化01Trie+线段树

$ \color{#0066ff}{ 题目描述 }$ 小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子. 小粽面前有 \(n\) 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 \(1\) 到 \(n\).第 \(i\) 种馅儿具有一个非负整数的属性值 \(a_i\).每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子.小粽准备用这些馅儿来做出 \(k\) 个粽子. 小粽的做法是:选两个整数数 \(l\), \(r\),满足 \(1 \leqslant l

P5283 [十二省联考2019]异或粽子 可持久化字典树

非常类似那道超级钢琴 维护一个可持久化01trie即可 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);--i) #define ll long long #define see(x) (cerr<<(#x)<<'='<<(x)<

[十二省联考2019]异或粽子(可持久化tire,堆)

[十二省联考2019]异或粽子(luogu) Description 题目描述 小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子. 小粽面前有 nn 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 11 到 nn.第 ii 种馅儿具有一个非负整数的属性值 a_iai?.每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子.小粽准备用这些馅儿来做出 kk 个粽子. 小粽的做法是:选两个整数数 ll, rr,满足 1 \leqslant l \leqslant r

[十二省联考2019] 异或粽子 解题报告 (可持久化Trie+堆)

interlinkage: https://www.luogu.org/problemnew/show/P5283 description: solution: 显然有$O(n^2)$的做法,前缀和优化一下即可 正解做法是先确定一个右端点$r$,找到最优的$l$使得该区间的异或和最大,这个可以用可持久化$Trie$实现.不懂的话可以在我的博客里搜索 对每个点取出来后把答案放进一个堆里,显然当前的堆顶一定会对答案产生贡献 然后我们考虑每次取出的右端点,它依旧可能产生贡献.即上一次取的最优的$l$把

[十二省联考2019]异或粽子 (可持久化01tire 堆)

/* 查询异或最大值的方法是前缀和一下, 在01trie上二分 那么我们可以对于n个位置每个地方先求出最大的数, 然后把n个信息扔到堆里, 当我们拿出某个位置的信息时, 将他去除当前最大后最大的信息插入到堆中 所以动态维护01trie就可以了 */ #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<queue> #define mm

luogu P5284 [十二省联考2019]字符串问题

传送门 如果某个串\(b\)是\(a_j\)的前缀,并且\(a_i\)支配\(b\),那么\(a_i\)后面就可以放\(a_j\),所以如果把对应的图建出来,问题就是求最长链,如果有环就是无限长 说到前缀,我们可以把所有\(a\)串建一棵\(Trie\),然后某个\(a_i\)支配的\(b\)串节点对应的子树内的\(a_j\)串节点都可以连边\((i,j)\).不过每次建\(Trie\)太慢了,我们可以利用\(SAM\)建后缀树,然后每次相当于一个点给某个点子树内的点连边,所以后缀树上父亲向儿子

luogu P5285 [十二省联考2019]骗分过样例

传送门 你管这玩意叫传统题? 1_998244353通过样例可以发现就是\(19^a\mod\ 998244353\),然后如果读入的数过大,根据费马小定理,那个读进来的数对\(998244352\)取模就行了 1?和1?+就是\(19^a\mod\ ?\),前者可以暴力求出,后者比较牛皮,可以找到数据中的两个数\(x,y(x<y)\),并且\(y-x\)较小,然后可以知道\(19^x,19^{y-x},19^y\),因为\(19^x*19^{y-x}\equiv 19^y\mod\ ?\),然

luogu P5289 [十二省联考2019]皮配

传送门 首先考虑一个正常的dp,设\(f_{i,j,k}\)为前\(i\)个学校,\(j\)人在\(\color{#0000FF}{蓝阵营}\),\(k\)人在\(\color{#654321}{吔}\)派系的方案,转移枚举选哪个导师就好了,注意一个城市要选同一阵营,所以可以多开一维\(0/1\)表示当前城市在哪个阵营 \(k=0\)的情况,可以发现选\(\color{#654321}{吔}\)派系的\(Yazid\)和\(Zayid\)都会增加\(\color{#654321}{吔}\)派系人