[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++ 语言中,异或操作符为^。

10棵丧心病狂的线段树

由于是xor很容易想到拆成10位来做,那么目标变成了统计1的个数。
于是对于一个区间的某一位,只需要维护所有前缀子区间、后缀子区间、任意子区间的0、1个数以及区间nim和,然后就可以做了。
我的方法常数大只能80分……

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10,mo=100000007;
struct dong{
    int l0[15],l1[15],r0[15],r1[15],a0[15],a1[15],num[15];
    void clear(){
        int i;
        fo(i,0,9) l0[i]=l1[i]=r0[i]=r1[i]=a0[i]=a1[i]=num[i]=0;
    }
};
dong tree[maxn*5];
int g[maxn*5];
int i,j,k,l,t,n,m,ans,top;
char ch;
dong zlt;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
char get(){
    char ch=getchar();
    while (ch!='M'&&ch!='Q') ch=getchar();
    return ch;
}
dong merge(dong a,dong b){
    dong c;
    int i;
    fo(i,0,9){
        c.l1[i]=a.l1[i]+b.l1[i]*(1^a.num[i])+b.l0[i]*(0^a.num[i]);
        c.l0[i]=a.l0[i]+b.l1[i]*(0^a.num[i])+b.l0[i]*(1^a.num[i]);
        c.r1[i]=b.r1[i]+a.r1[i]*(1^b.num[i])+a.r0[i]*(0^b.num[i]);
        c.r0[i]=b.r0[i]+a.r1[i]*(0^b.num[i])+a.r0[i]*(1^b.num[i]);
        c.a1[i]=a.a1[i]+b.a1[i]+a.r1[i]*b.l0[i]+a.r0[i]*b.l1[i];
        c.a0[i]=a.a0[i]+b.a0[i]+a.r1[i]*b.l1[i]+a.r0[i]*b.l0[i];
        c.num[i]=a.num[i]^b.num[i];
    }
    return c;
}
void change(int p,int l,int r,int a,int b){
    if (l==r){
        tree[p].clear();
        int i,j=b;
        fo(i,0,9){
            tree[p].l1[i]=tree[p].r1[i]=tree[p].a1[i]=(j%2==1);
            tree[p].l0[i]=tree[p].r0[i]=tree[p].a0[i]=(j%2==0);
            tree[p].num[i]=j%2;
            j/=2;
        }
        return;
    }
    int mid=(l+r)/2;
    if (a<=mid) change(p*2,l,mid,a,b);else change(p*2+1,mid+1,r,a,b);
    tree[p]=merge(tree[p*2],tree[p*2+1]);
}
void query(int p,int l,int r,int a,int b){
    if (l==a&&r==b){
        g[++top]=p;
        return;
    }
    int mid=(l+r)/2;
    if (b<=mid) query(p*2,l,mid,a,b);
    else if (a>mid) query(p*2+1,mid+1,r,a,b);
    else query(p*2,l,mid,a,mid),query(p*2+1,mid+1,r,mid+1,b);
}
int main(){
    n=read();
    fo(i,1,n){
        j=read();
        change(1,1,n,i,j);
    }
    m=read();
    while (m--){
        ch=get();
        j=read();k=read();
        if (ch=='M') change(1,1,n,j,k);
        else{
            if (j>k){
                printf("0\n");
                continue;
            }
            top=0;
            query(1,1,n,j,k);
            zlt=tree[g[1]];
            fo(i,2,top) zlt=merge(zlt,tree[g[i]]);
            ans=0;
            t=1;
            fo(l,0,9){
                ans=(ans+ll(t)*ll(zlt.a1[l])%mo)%mo;
                t*=2;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值