loj6100 「2017 山东二轮集训 Day1」第一题

传送门:https://loj.ac/problem/6100

【题解】

我们考虑维护从某个端点开始的最长满足条件的长度,如果知道了这个东西显然我们可以用主席树来对每个节点建棵关于右端点的权值线段树,然后区间修改,标记永久化,询问就可以差分了

考虑如何求出某个端点开始的最长满足条件的长度,也就是某个端点$i$开始,到nxt[i]的这一段都满足异或不减性质。

考虑异或什么时候会导致减法:修改了最高位的时候

我们令s[x][i][j]表示$1 \sim i$个位置,二进制下第j位被当做最高位的时候,被操作了几次,$x$为0或1。0表示原来是1,变成0的次数;1表示原来是0,变成1的次数。

那么我们每次找出最高位就能更新了。

我们还要维护一个前缀异或值来进行操作。

然后我们二分右端点,考虑如何判断区间$[l,r]$是否合法。

对于某一位,如果在$l-1$的时候,前缀异或值为1,那么从$l$开始,相当于没有这个前缀异或值,也就是当修改从0改成1的时候,由于我们是假装他有“1”的前缀异或值,来进行s的操作的,所以s从0改成1相当于实际的1改成0,也就是减小。

同理对于在$l-1$的时候,前缀异或值为0,也进行类似操作判断即可。

那么这个复杂度是$O(logn)$的,因为有log位要判断。

预处理复杂度$O(nlog^2n)$

挺好写的吧。。

# include 
# include <string.h>
# include 
# include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;

const int M = 2e5 + 10, N = 1e5 + 10;
const int mod = 1e9 + 7, Max = 5e6 + 10;

int n, a[N], nxt[N];
int s[2][N][66], sxor[N];
// 前i个数,第j位为最高位的时候,有多少次从1变成0 (1),以及从0变成1 (0). 

# define bit(x, i) (((x) >> (i)) & 1)
# define len(l, r) ((r) - (l) + 1)

inline bool chk(int l, int r) {
    for (int i=30; ~i; --i) { 
        if(bit(sxor[l-1], i) && s[0][r][i] - s[0][l-1][i] > 0) return false;
        if(!bit(sxor[l-1], i) && s[1][r][i] - s[1][l-1][i] > 0) return false;
    }
    return true;
}

inline bool chk_force(int l, int r) {
    int cur, lst; cur = lst = 0;
    for (int i=l; i<=r; ++i) {
        cur = lst ^ a[i];
        if(cur < lst) return false;
        lst = cur;
    }
    return true;
}

inline int gh(int x) {
    for (int i=30; ~i; --i)
        if(bit(x, i)) return i;
    return -1;
} 

inline int gnext(int pos) {
    int l=pos, r=n, mid;
    while(1) {
        if(r-l <= 3) {
            for (int i=r; i>=l; --i)
                if(chk(pos, i)) return i;
            break;
        }
        mid = l+r>>1;
        if(chk(pos, mid)) l = mid;
        else r = mid;
    }
    return -1;
}

int rt[M];
struct CMT {
    int ch[Max][2], tag[Max], siz;
    ll s[Max];
    
    # define ls ch[x][0]
    # define rs ch[x][1]
    
    inline void set() {
        siz = 0;
        memset(tag, 0, sizeof tag);
        memset(s, 0, sizeof s);
    }
    
    inline void edt(int &x, int y, int l, int r, int L, int R, int d) {
        x = ++siz; ch[x][0] = ch[y][0], ch[x][1] = ch[y][1];
        s[x] = s[y]; tag[x] = tag[y];
        if(L == l && r == R) {
            tag[x] += d;
            return ;
        }
        s[x] += (ll)d * (R-L+1);
        int mid = l+r>>1;
        if(R <= mid) edt(ls, ch[y][0], l, mid, L, R, d);
        else if(L > mid) edt(rs, ch[y][1], mid+1, r, L, R, d);
        else edt(ls, ch[y][0], l, mid, L, mid, d), edt(rs, ch[y][1], mid+1, r, mid+1, R, d);
    }
    
    inline ll query(int x, int y, int l, int r, int L, int R) {
        ll ret = (ll)(tag[y] - tag[x]) * (R-L+1);
        if(L == l && r == R) return ret + s[y] - s[x];
        int mid = l+r>>1;
        if(R <= mid) return ret + query(ls, ch[y][0], l, mid, L, R);
        else if(L > mid) return ret + query(rs, ch[y][1], mid+1, r, L, R);
        else return ret + query(ls, ch[y][0], l, mid, L, mid) + query(rs, ch[y][1], mid+1, r, mid+1, R);
    }    
}T;

int main() {
    cin >> n; sxor[0] = 0;
    for (int i=1, h; i<=n; ++i) {
        scanf("%d", a+i); sxor[i] = sxor[i-1] ^ a[i];
        for (int j=0; j<=30; ++j) s[0][i][j] = s[0][i-1][j], s[1][i][j] = s[1][i-1][j];
        h = gh(a[i]);
        if(h != -1) {
            if(bit(sxor[i-1], h)) s[1][i][h] ++;
            else s[0][i][h] ++;
        } 
    }
    
    for (int i=1; i<=n; ++i) nxt[i] = gnext(i);
    
    T.set();
    for (int i=1; i<=n; ++i) T.edt(rt[i], rt[i-1], 1, n, i, nxt[i], 1);
    
    
    int Q; cin >> Q;
    ll lst = 0; int l, r;
    while(Q--) {
        scanf("%d%d", &l, &r);
        l = (l+lst)%n+1, r = (r+lst)%n+1;
        if(l>r) swap(l, r);
        lst = T.query(rt[l-1], rt[r], 1, n, l, r);
        printf("%lld\n", lst);
        lst %= n;
    }


    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/galaxies/p/loj6100.html

你可能感兴趣的:(loj6100 「2017 山东二轮集训 Day1」第一题)