首页 > 其他 > 详细

[JZOJ3339]【NOI2013模拟】wyl8899和法法塔的游戏

时间:2019-07-09 23:07:51      阅读:92      评论:0      收藏:0      [点我收藏+]

题目

题目大意

给你一个数列,每次给出\(r,a,b\),你要找到\(l\in [a,b]\)使得\([l,r-1]\)的异或和最小,
并且要修改\(r\)位置的数。


思考历程

当我看到这题的时候,已经没有什么时间了……
这题需要一点点的博弈基础(题目大意直接将它省掉了),不过还比较简单,就连我这样的博弈白痴都能会。
搞出了之后就来了个最裸的暴力,交了上去。
WA了……
后面发现是答案为\(-1\)的时候我进行了修改操作……改了之后TLE63……


水法

说实在的这数据真的太水了,也是大把大把的人用暴力过去了。
首先暴力方法加一点点优化就能过了(我在打正解的时候打了个暴力对照一下,将这个暴力交上去,AC……)
接下来介绍一下LYL的分类讨论大法:
有两种暴力方式,一种是直接暴力,一种是求前缀和暴力。
LYL在程序中计算了两种暴力的时间,哪种暴力更优秀就使用哪种暴力……
然后轻轻松松地水过了这题……


正解

正解的做法看起来比暴力复杂多了,是分块套\(Trie\)
其实也很简单。先前缀和一遍,于是问题就转化成了在\([a-1,b-1]\) 中找到某个数使得它异或\(pre_{y-1}\)最小。
对于每个块建个\(Trie\),如果询问整个块,就在\(Trie\)上面找。如果修改,就打个标记。
对于散块直接拆掉,暴力重构。
时间复杂度为\(O(m\sqrt n *10)\),比较优秀……
然而跑不过暴力……


总结

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <climits>
#include <cassert>
#define N 100010
#define maxK 320
int n,m,K;
int st[N],pre[N];
int r[maxK],bel[N];
struct Node{
    Node *c[2];
} d[32000001];
int cnt;
Node *root[maxK];
inline void insert(Node *t,int x){
    for (int i=9;i>=0;--i){
        if (!t->c[x>>i&1])
            t->c[x>>i&1]=&d[++cnt];
        t=t->c[x>>i&1];
    }
}
int lazy[maxK];
inline void reduce(int k){
    for (int i=r[k-1]+1;i<=r[k];++i)
        pre[i]^=lazy[k];
    lazy[k]=0;
}
inline void rebuild(int k){
    root[k]=&d[++cnt];
    for (int i=r[k-1]+1;i<=r[k];++i)
        insert(root[k],pre[i]);
}
inline int find_min(Node *t,int y){
    int res=0;
    for (int i=9;i>=0;--i)
        if (t->c[y>>i&1])
            t=t->c[y>>i&1];
        else{
            t=t->c[y>>i&1^1];
            res+=1<<i;
        }
    return res;
}
inline int get(int x){return pre[x]^lazy[bel[x]];}
inline int query(int y,int a,int b){
    int res=INT_MAX,aa=bel[a],bb=bel[b];
    if (aa==bb){
        if (lazy[aa])
            reduce(aa),rebuild(aa);
        for (int i=a;i<=b && res;++i)
            res=min(res,pre[i]^y);
        return res;
    }
    if (lazy[aa])
        reduce(aa),rebuild(aa);
    if (lazy[bb])
        reduce(bb),rebuild(bb);
    for (int i=a;i<=r[aa] && res;++i)
        res=min(res,pre[i]^y);
    for (int i=r[bb-1]+1;i<=b && res;++i)
        res=min(res,pre[i]^y);
    for (int i=aa+1;i<=bb-1 && res;++i)
        res=min(res,find_min(root[i],y^lazy[i]));
    return res;
}
inline void change(int l,int c){
    int ll=bel[l];
    if (lazy[ll])
        reduce(ll);
    for (int i=l;i<=r[ll];++i)
        pre[i]^=c;
    rebuild(ll);
    for (int i=ll+1;i<=m;++i)
        lazy[i]^=c;
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
        scanf("%d",&st[i]),pre[i]=pre[i-1]^st[i];
    K=sqrt(n);
    for (int i=1;i*K<=n;++i)
        r[++m]=i*K;
    if (n%K)
        r[++m]=n;
    for (int i=1;i<=m;++i){
        root[i]=&d[++cnt];
        for (int j=r[i-1]+1;j<=r[i];++j){
            insert(root[i],pre[j]);
            bel[j]=i;
        }
    }
    int T;
    scanf("%d",&T);
    while (T--){
        int y,a,b;
        scanf("%d%d%d",&y,&a,&b);
        int xo=query(get(y-1),a-1,b-1),v=get(y)^get(y-1);
        if (xo<v){
            printf("%d\n",v-xo);
            change(y,v^xo);
        }
        else
            printf("-1\n");
    }
    return 0;
}

总结

做题的时候要试着BFS地来做题……这样就可以早点发现这题可做了……
不好维护的东西可以考虑一下分块……

[JZOJ3339]【NOI2013模拟】wyl8899和法法塔的游戏

原文:https://www.cnblogs.com/jz-597/p/11160975.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!