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

可持久化线段树+二分

注意到如果[l,r]不降,[l,r-1]就肯定也不降,因此如果能对于每一个位置l,找到最远的r满足不降。这样套上主席树问题就搞定了。

会降当且仅当此时异或上的a[i]的最高位恰好把当前的这一位从1变成0。因此记 f[i][j][0/1] 表示从a[1]开始异或到a[i],以j为最高位的时候,j从1变成0(或0变成1)有多少次。这样二分以后每一位都判一下即可。 O(nlog2n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define H 32
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    int n, a[N], sum[N], f[N][H][2], pos[N];

    struct seg
    {
        seg *ch[2];
        ll sum_v, sum_1, sum_i;
    }mem[N*25], *tot, *root[N], *null;
    seg *newseg()
    {
        seg *x = ++tot; *x = *null;
        return x;
    }
    void init()
    {
        null = tot = mem; *null = (seg){{null,null},0,0,0};
        root[0] = newseg();
    }
    void insert(seg *x, seg *y, int l, int r, int p, int v, int i)
    {
        if(l == r)
        {
            *y = *x;
            y->sum_v += v;
            y->sum_i += i;
            y->sum_1 += 1;
            return;
        }
        int mid = (l+r)>>1;
        if(p <= mid) {y->ch[0] = newseg(), y->ch[1] = x->ch[1]; insert(x->ch[0], y->ch[0], l, mid, p, v, i);}
        else {y->ch[1] = newseg(), y->ch[0] = x->ch[0]; insert(x->ch[1], y->ch[1], mid+1, r, p, v, i);}
        y->sum_v = y->ch[0]->sum_v + y->ch[1]->sum_v;
        y->sum_i = y->ch[0]->sum_i + y->ch[1]->sum_i;
        y->sum_1 = y->ch[0]->sum_1 + y->ch[1]->sum_1;
    }
    ll query_v(seg *x, seg *y, int l, int r, int ql, int qr)
    {
        if(ql <= l && r <= qr) return y->sum_v - x->sum_v; int mid = (l+r)>>1; ll ret = 0;
        if(ql <= mid) ret += query_v(x->ch[0], y->ch[0], l, mid, ql, qr);
        if(mid <  qr) ret += query_v(x->ch[1], y->ch[1], mid+1, r, ql, qr);
        return ret;
    }
    void query_i1(seg *x, seg *y, int l, int r, int ql, int qr, ll &sum_i, ll &sum_1)
    {
        if(ql <= l && r <= qr) {sum_i += y->sum_i-x->sum_i, sum_1 += y->sum_1-x->sum_1; return;} int mid = (l+r)>>1;
        if(ql <= mid) query_i1(x->ch[0], y->ch[0], l, mid, ql, qr, sum_i, sum_1);
        if(mid <  qr) query_i1(x->ch[1], y->ch[1], mid+1, r, ql, qr, sum_i, sum_1);
    }

    int check(int l, int r)
    {
        for(int h = 31; h; h--)
        {
            if(((sum[l]>>(h-1))&1)^((a[l]>>(h-1))&1)){if(f[r][h][1]-f[l][h][1] > 0) return 0;}
            else {if(f[r][h][0]-f[l][h][0] > 0) return 0;}
        }
        return 1;
    }
    int find_bit(int x)
    {
        for(int h = 1<<30, i = 31; h; h >>= 1, i--)
            if(x & h) return i;
        return 0;
    }
    void main()
    {
        scanf("%d",&n);
        for(int i = 1; i <= n; i++) 
        {
            scanf("%d",&a[i]);
            sum[i] = sum[i-1] ^ a[i];
            memcpy(f[i], f[i-1], sizeof(f[i]));
            int h = find_bit(a[i]);
            f[i][h][(sum[i]>>(h-1))&1]++;
        }
        for(int i = 1; i <= n; i++)
        {
            int l = i, r = n;
            for(; l < r; )
            {
                int mid = (l+r+1)>>1;
                if(check(i,mid)) l = mid;
                else r = mid - 1;
            }
            pos[i] = l;
        }
        init(); for(int i = 1; i <= n; i++) insert(root[i-1], root[i] = newseg(), 1, n, pos[i], pos[i]-i+1, i-1);

        int qcnt; ll ans = 0; scanf("%d",&qcnt);
        for(; qcnt--; )
        {
            int l, r; scanf("%d%d",&l,&r); 
            l = (l+ans)%n+1; r = (r+ans)%n+1;
            if(l > r) swap(l, r);
            ans = 0; ll sum_i = 0, sum_1 = 0;
            ans += query_v(root[l-1], root[r], 1, n, l, r);
            if(r < n) query_i1(root[l-1], root[r], 1, n, r+1, n, sum_i, sum_1);
            ans += r*sum_1-sum_i;
            printf("%lld\n",ans);
        }
    }
}
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值