• 2019 各省省选试题选做


    蒟蒻快活不了了,必须找点题摩擦了

    记录表

    省份 d1t1 d1t2 d1t3 d2t1 d2t2 d2t3
    十二省联考 $√$ $√$        
    ZJOI            
    HNOI            
    GX/GZOI            
    BJOI $√$          
    SNOI             
    JSOI             
    TJOI             
    SDOI             

     注意事项:

    1. GZOI 不是广州 OI,是贵州 OI!

    2. 本文只贴签到题的完整题解,较长的题解放新文链接。


    十二省联考

    d1t1 异或粽子

    签到题

    这道题有很像的原题而且做法没什么区别

    我这么菜甚至以前还出过这道题

    好吧,总之就是这题很送

    可持久化 trie,第 $i$ 个版本表示后缀 $[i,n]$ 中,第一大前缀异或和。用一个堆,开 $pair$ 存放 $[1...n,n]$ 这 $n$ 个后缀的最大前缀和 及每个最大前缀和对应的后缀的左端点,然后弹 $k-1$ 次堆顶,每次弹堆顶时 把堆顶那个最大前缀和对应的后缀求下一大前缀和,将其存入堆(若这个后缀中的所有前缀都被弹过,就忽略关于下一大前缀和的操作)。第 $k$ 次的堆顶就是答案了。

    这样做的正确性显然:你正常的想法就是进行 $k-1$ 轮这样的操作:找到序列的最大异或和,删掉它。而任何时刻,序列的最大异或和都必定对应一个区间,也就对应一个左端点,那么我们只要同时维护所有左端点对应的后缀的答案,再取最优就行了。

    时间复杂度 $O(k imes log(n))$。

    我的 code(把以前自己写的标程拷过来改一改)

     1 #pragma GCC optimize(2)
     2 #include<bits/stdc++.h>
     3 #define rep(i,x,y) for(int i=(x);i<=(y);++i)
     4 //#define int long long
     5 //#define uint long long
     6 #define ll long long
     7 #define N 500002
     8 #define pii pair<uint,int>
     9 #define mp make_pair
    10 using namespace std;
    11 inline uint read(){
    12     uint x=0; bool f=1; char c=getchar();
    13     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    14     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    15     if(f) return x;
    16     return 0-x;
    17 }
    18 int n,k,num[N][34],rt[N],rk[N],cnt;
    19 uint a,sum[N];
    20 struct Tree{int siz,son[2];}tr[N*34];
    21 void modify(int lst,int &o,int x,int step){
    22     if(!o) o=++cnt, tr[o]=tr[lst];
    23     tr[o].siz++;
    24     if(!step) return;
    25     if(!num[x][step]) tr[o].son[0]=0, modify(tr[lst].son[0], tr[o].son[0], x, step-1);
    26     else tr[o].son[1]=0, modify(tr[lst].son[1], tr[o].son[1], x, step-1);
    27 }
    28 uint query(int o,int x,int k,uint v,int step){
    29     assert(o);
    30     if(!step) return v;
    31     int s=num[x][step]^1;
    32     if(tr[tr[o].son[s]].siz>=k) return query(tr[o].son[s], x, k, v+(1ll<<(step-1)), step-1);
    33     else return query(tr[o].son[s^1], x, k-tr[tr[o].son[s]].siz, v, step-1);
    34 }
    35 priority_queue<pii> pq;
    36 ll ans;
    37 signed main(){
    38     n=read(), k=read();
    39     modify(0,rt[0],0,32);
    40     rep(i,1,n){
    41         a=read(), sum[i]=sum[i-1]^a;
    42         a=sum[i];
    43         rep(j,1,32) num[i][j]=0;
    44         int cnt=0;
    45         while(a>0) num[i][++cnt]=a&1, a>>=1;
    46         rk[i]=1, pq.push(mp(query(rt[i-1],i,1,0,32),i));
    47         modify(rt[i-1],rt[i],i,32);
    48     }
    49     pii cur;
    50     for(int i=1;i<k;++i){
    51         cur=pq.top(), pq.pop();
    52         ans+=cur.first;
    53         int x=cur.second;
    54         if(rk[x]+1<=x) pq.push(mp(query(rt[x-1],x,++rk[x],0,32),x));
    55     }
    56     cout<<ans+pq.top().first;
    57     return 0;
    58 }
    59 /*
    60 3 3
    61 2 2 4
    62 */
    View Code

    题外话:

    1. 这题可以同时在 $n$ 个版本的 $trie$ 树上二分($syf$ 的做法)

    2. 我出的那道题可以二分

    d1t2 字符串问题

    链接

    d1t3 骗分过样例

    链接

    d2t1 皮配

    d2t2 春节十二响

    签到题

    不难发现,对于一条在根拐弯的链,设其左链比右链长,其答案就是 max{ 左链的最大 $M_i$ 值,右链的最大 $M_i$ 值 } + max{ 左链的第二大 $M_i$ 值,右链的第二大 $M_i$ 值} + ... + max{ 左链的第 ? 大 $M_i$ 值,右链的最小 $M_i$ 值 } + 左链其余更小的 $M_i$ 值之和 + 根的 $M_i$ 值。右链比左链长同理。

    这样贪心的正确性显然可以反证出来。

    拓展到树上,我们相当于要从叶子往根“合并”每棵子树的堆,其中一棵子树的堆 存的是这棵子树的所有段的大小,堆的大小显然是这棵子树的最深点的深度(至少要用这么多个段,在此基础上让段的数量更少,即合并更多的子程序,答案显然会更优)。

    这里的“合并”不是把两个大小为 $a$ 和 $b$ 的堆合并成大小为 $a+b$ 的堆的那个“合并”,而是这样的:在两个堆的最大值中取大的那个放入新堆,删掉两个堆的最大值,再取两个堆的最大值中取大的那个放入新堆,再删掉两个堆的最大值……直到有一个堆为空,然后把另一个堆剩下的数都放入新堆。一个子树的根 “合并”完其所有儿子子树的堆之后,会把它自己的 $M_i$ 值加入堆(显然根只能独自在一个段)。其实就是模拟之前说的那个 统计在根拐弯的链的答案的步骤

    然而暴力“合并”的复杂度好像是错的。

    但你仔细回顾前文就会发现,只需要使用适当的合并方法,按点的深度从大往小合并,一个点在合并其所有儿子子树时 先 $O(1)$ 继承其 $size$ 值最大的儿子堆,就能过了。

    原因很显然——如 $Destinies$ 大佬所说,根据长链剖分的原理,这题的堆“合并”实际上就是长链剖分的数组合并!原树中每个点只会被合并一次!

    所以时空复杂度直接参考长链剖分。时间复杂度再套上每次求堆顶的 $log$,是 $O(nlog{n})$ 的,不是 $O(nlog^2{n})$ 的!!!

     1 #include<bits/stdc++.h>
     2 #define rep(i,x,y) for(int i=(x);i<=(y);++i)
     3 #define dwn(i,x,y) for(int i=(x);i>=(y);--i)
     4 #define rep_e(i,u) for(int i=hd[u];i;i=e[i].nxt)
     5 #define ll long long
     6 #define N 200002
     7 using namespace std;
     8 inline int read(){
     9     int x=0; bool f=1; char c=getchar();
    10     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    11     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    12     if(f) return x;
    13     return 0-x;
    14 }
    15 int n,a[N],pos[N],tmp[N];
    16 priority_queue<int>son[N];
    17 struct edge{int v,nxt;}e[N<<1];
    18 int hd[N],cnt;
    19 inline void add(int u,int v){e[++cnt]=(edge){v,hd[u]}, hd[u]=cnt;}
    20 void dfs(int u,int fa){
    21     pos[u]=u;
    22     rep_e(i,u) if(e[i].v!=fa){
    23         dfs(e[i].v,u);
    24         if(i==hd[u]) pos[u]=pos[e[i].v];
    25         else{
    26             if(son[pos[u]].size()<son[pos[e[i].v]].size()) swap(pos[u],pos[e[i].v]);
    27             int x=0;
    28             while(!son[pos[e[i].v]].empty()){
    29                 tmp[++x]=max(son[pos[u]].top(),son[pos[e[i].v]].top());
    30                 son[pos[u]].pop(), son[pos[e[i].v]].pop();
    31             }
    32             while(x) son[pos[u]].push(tmp[x--]);
    33         }
    34     }
    35     son[pos[u]].push(a[u]);
    36 }
    37 int main(){
    38     n=read();
    39     int f;
    40     rep(i,1,n) a[i]=read();
    41     rep(i,2,n) f=read(), add(f,i), add(i,f);
    42     dfs(1,0);
    43     ll ans=0;
    44     while(!son[pos[1]].empty()) ans+=son[pos[1]].top(), son[pos[1]].pop();
    45     cout<<ans<<endl;
    46     return 0;
    47 }
    View Code

    顺便说个事情,开 $c++11$ 的话,std::swappriority queue::swap 的复杂度都是 $O(1)$ 的(不开 $c++11$ 交换两个数组或堆之类的东西就是 $O(n)$ 的了)。详见zsy的博客

    d2t3 希望

    BJOI

    d1t1 奥术神杖

    链接

    d1t2 勘破神机

  • 相关阅读:
    PPP点对点协议
    Wireshark包过滤
    asp.net mvc 简单项目框架的搭建过程(一)对Bll层和Dal层进行充分解耦
    sql学习笔记(三)—— 联表查询
    Sql学习笔记(二)—— 条件查询
    bzoj 1696: [Usaco2007 Feb]Building A New Barn新牛舍 ——中位数排序
    汕头市队赛 SRM19 字符题
    bzoj 2037: [Sdoi2008]Sue的小球——dp
    bzoj 1914: [Usaco2010 OPen]Triangle Counting 数三角形——极角排序
    【BZOJ】3302: [Shoi2005]树的双中心 && 2103: Fire 消防站 && 2447: 消防站
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/xoi2019.html
Copyright © 2020-2023  润新知