【JZOJ4359】【GDKOI2016】魔卡少女

题目描述

君君是中山大学的四年级学生。有一天在家不小心开启了放置在爸爸书房中的一本古书。于是,君君把放在书中最上面的一张牌拿出来观摩了一下,突然掀起一阵大风把书中的其她所有牌吹散到各地。这时一只看上去四不像的可爱生物“封印之兽”可鲁贝洛斯从书中钻了出来,它告诉君君书中的牌叫“库洛牌”,现在散落各地已实体化,要君君将它们全部再次封印起来,以免危害世界,于是君君开始过上了收服“库洛牌”的旅程。
经过不懈努力,君君集齐了N 张库洛牌,最后的审判就要来临,为了战胜审判者月,君君开始研究起这N 张库洛牌的魔法效果。君君已经将N 张库洛牌从左到右依次排列好,这N 张库洛牌的魔法值从左到右依次为a1; a2; a3; …aN。她将告诉你这N 张库洛牌的魔法值。在最后的审判时,审判者月将会选择一个区间进行PK,君君预测了可能进行PK 的若干区间,她想请你帮助她计算这些区间的魔法效果,以便她更好地布置战术。一个区间内,所有连续子序列都会产生魔法效果。一个连续子序列p1; p2; p3; …; pk 的魔法效果定义为p1^p2^p3^…^pk(^表示异或)。一个区间的魔法效果定义为所有连续子序列的魔法效果的和。例如有5 张库洛牌,魔法值为1; 1; 2; 4; 5,询问区间[2; 4] 的魔法效果。区间[2; 4] 包含的连续子序列为‘‘1”; ‘‘2”; ‘‘4”; ‘‘1; 2”; ‘‘2; 4”; ‘‘1; 2; 4”, 它们的魔法值分别为1; 2; 4; 3; 6; 7,所以区间[2,4] 的魔法效果为1 + 2 + 4 + 3 + 6 + 7 = 23。
库洛牌的魔法效果狂拽炫酷吊炸天,这个值可能很大,所以你只需要输出这个值模100,000,007。另外,任性的君君可以在询问的过程中对库洛牌的魔法值进行修改。
现在,君君给出了M 个操作,操作格式如下:
1. M p x 表示将第p 张库洛牌的魔法值修改为x。
2. Q l r 表示询问区间[l; r] 的魔法效果。
Pascal 语言中,异或操作符为xor,C++ 语言中,异或操作符为^。

数据范围

30% 的数据:N,M<=300
另外20% 的数据:N,M<=30000, 操作1 的数量不超过50
80% 的数据:N,M<=30000
100% 的数据:N<=100000;M<=100000; 0<=ai, x<=1000

解法

位运算考虑按位操作,
使用线段树维护。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define sqr(x) ((x)*(x))
#define ln(x,y) int(log(x)/log(y))
#define bit(x,y) ((x)&(1<<(y)))
using namespace std;
const char* fin="ex4359.in";
const char* fout="ex4359.out";
const int inf=0x7fffffff;
const int maxn=100007,maxt=maxn*4,maxk=10,mo=100000007;
int n,m,i,j,k,l;
int a[maxn];
struct point{
    unsigned int l[2],r[2],m[2],x;
}nil;
void work(point &a,int v1,int key){
    if (bit(v1,key)) a.l[0]=a.r[0]=a.m[0]=0,a.l[1]=a.r[1]=a.m[1]=a.x=1;
    else a.l[0]=a.r[0]=a.m[0]=1,a.l[1]=a.r[1]=a.m[1]=a.x=0;
}
point merge(point a,point b){
    point c;
    int i;
    for (i=0;i<=1;i++){
        c.l[i]=a.l[i]+b.l[i^a.x];
        c.r[i]=a.r[i^b.x]+b.r[i];
        c.m[i]=a.m[i]+b.m[i]+a.r[0]*b.l[i^0]+a.r[1]*b.l[1^i];
    }
    c.x=a.x^b.x;
    return c;
}
struct tree{
    int key;
    point c[maxt][maxk];
    void plant(int l,int r,int t){
        int mid=(l+r)/2;
        if (l==r){
            for (key=0;key<10;key++) work(c[t][key],a[l],key);
            return;
        }
        plant(l,mid,t*2);
        plant(mid+1,r,t*2+1);
        for (key=0;key<10;key++) c[t][key]=merge(c[t*2][key],c[t*2+1][key]);
    }
    point find(int l,int r,int t,int v1,int v2,int key){
        int mid=(l+r)/2;
        if (l>v2 || r<v1) return nil;
        if (l>=v1 && r<=v2) return c[t][key];
        return merge(find(l,mid,t*2,v1,v2,key),find(mid+1,r,t*2+1,v1,v2,key));
    }
    void change(int l,int r,int t,int v,int v1){
        int mid=(l+r)/2;
        if (l==r){
            for (key=0;key<10;key++) work(c[t][key],v1,key);
            return ;
        }
        if (v<=mid) change(l,mid,t*2,v,v1);
        else change(mid+1,r,t*2+1,v,v1);
        for (key=0;key<10;key++) c[t][key]=merge(c[t*2][key],c[t*2+1][key]);
    }
}T;
int main(){
    scanf("%d",&n);
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    T.plant(1,n,1);
    scanf("%d\n",&m);
    for (i=1;i<=m;i++){
        char ch;
        scanf("%c",&ch);
        scanf("%d%d\n",&j,&k);
        if (ch=='Q'){
            ll ans=0;
            for (l=0;l<10;l++){
                ans=(ans+(ll)T.find(1,n,1,j,k,l).m[1]*(1<<l))%mo;
            }
            printf("%lld\n",ans);
        }else{
            T.change(1,n,1,j,k);
        }
    }
    return 0;
}

启发

线段树可以说是一种可修改的分治算法。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值