zoukankan      html  css  js  c++  java
  • cf1076G. Array Game

    题目描述

    题解

    考虑到每个点被跳到后都会 $-1$ ,所以我们一开始就把整个序列都 $-1$ ,然后玩法就变成要么跳到 $[i+1,\min(k,i+m)]$ 上的点,要么自身权值 $-1$ 。

    然后显然可以直接转化为每个点为 $0$ 或 $1$ 。

    所以考虑 $\text{dp}$ : $f_{i,0/1}$ 表示第 $i$ 个点为 $0/1$ ,先手是否能够必胜,考虑转移:

    $$f_{i,0}=\text{or}\{f_{i+x,a_{i+x}} \text{xor}\ 1\}$$$$f_{i,1}=\text{or}\{f_{i+x,a_{i+x}} \text{xor}\ 1,f_{i,0}\text{xor}\ 1\}$$

    因为 $m$ 很小,所以我们可以考虑线段树维护这个 $\text{dp}$ ,具体来说就是 $[l,r]$ 区间,维护 $f_S$ 表示 $r+1,r+2,...,r+m$ 的必胜状态为 $S$ 的情况下, $l,l+1,...,l+m-1$ 的必胜状态。于是可以 $O(2^m)$ 合并。

    然后考虑区间加的操作,加偶数没影响,加奇数的话实际上就是把 $\text{dp}$ 值互换,所以只要再同时维护相反的 $\text{dp}$ 值即可。

    然后我们可以发现其实我们只关心最近的必输状态的点离端点的距离,如果 $>m$ 了就记 $m+1$ ,因此就只要 $O(m)$ 合并即可。

    效率: $O(nm\log n)$ 。

    代码

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int N=2e5+5;
    int n,m,q,a[N],tg[N<<2];
    struct O{int s[7];}t[2][N<<2];
    #define Ls k<<1
    #define Rs k<<1|1
    #define mid ((l+r)>>1)
    O hb(O u,O v){
        O w;
        for (int i=1;i<=m+1;i++)
            w.s[i]=u.s[v.s[i]];
        return w;
    }
    void up(int k){
        t[0][k]=hb(t[0][Ls],t[0][Rs]);
        t[1][k]=hb(t[1][Ls],t[1][Rs]);
    }
    void build(int k,int l,int r){
        if (l==r){
            for (int i=1;i<=m;i++)
                t[a[l]][k].s[i]=t[!a[l]][k].s[i]=i+1;
            t[a[l]][k].s[m+1]=1;
            t[!a[l]][k].s[m+1]=m+1;
            return;
        }
        build(Ls,l,mid);
        build(Rs,mid+1,r);up(k);
    }
    void push(int k){
        swap(t[0][k],t[1][k]);tg[k]^=1;
    }
    void down(int k){
        push(Ls);push(Rs);tg[k]=0;
    }
    void upd(int k,int l,int r,int L,int R){
        if (L<=l && r<=R) return push(k);
        if (tg[k]) down(k);
        if (mid>=L) upd(Ls,l,mid,L,R);
        if (mid<R) upd(Rs,mid+1,r,L,R);
        up(k);
    }
    O qry(int k,int l,int r,int L,int R){
        if (L<=l && r<=R) return t[0][k];
        if (tg[k]) down(k);
        if (mid>=R) return qry(Ls,l,mid,L,R);
        if (mid<L) return qry(Rs,mid+1,r,L,R);
        return hb(qry(Ls,l,mid,L,R),qry(Rs,mid+1,r,L,R));
    }
    int main(){
        scanf("%d%d%d",&n,&m,&q);
        for (int i=1;i<=n;i++){
            LL v;scanf("%lld",&v);
            v--;a[i]=(v&1ll);
        }
        build(1,1,n);
        for (int op,l,r;q--;){
            scanf("%d%d%d",&op,&l,&r);
            if (op&1){
                LL v;scanf("%lld",&v);
                if (v&1ll) upd(1,1,n,l,r);
            }
            else{
                O u=qry(1,1,n,l,r);
                printf("%d\n",1+(u.s[m+1]==1));
            }
        }
        return 0;
    }
  • 相关阅读:
    算法导论第十三章:红黑树
    算法导论:相关数学问题小结
    STL_源码剖析之三:迭代器与traits
    算法导论第十二章:二叉查找树
    Common Lisp专家Peter Seibel对Google公司首席Java架构师Joshua Bloch的访谈(摘一段)
    STL源码剖析之六:算法
    STL 源码剖析之四:序列式容器
    算法导论第十四章:数据结构的扩张
    链表的归并排序:来自STL_ list_ sort 算法
    STL源码剖析之五:关联式容器
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/15536467.html
Copyright © 2011-2022 走看看