GDKOI2016 魔卡少女
Description
君君是中山大学的四年级学生。有一天在家不小心开启了放置在爸爸书房中的一本古书。于是,君君把放在书中最上面的一张牌拿出来观摩了一下,突然掀起一阵大风把书中的其她所有牌吹散到各地。这时一只看上去四不像的可爱生物“封印之兽”可鲁贝洛斯从书中钻了出来,它告诉君君书中的牌叫“库洛牌”,现在散落各地已实体化,要君君将它们全部再次封印起来,以免危害世界,于是君君开始过上了收服“库洛牌”的旅程。
经过不懈努力,君君集齐了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++ 语言中,异或操作符为^。
Input
第一行为一个整数N,表示有N 张库洛牌。
第二行为N 个整数,表示一开始N 张库洛牌的值。
第三行为一个整数M,表示有M 个操作。
接下来M 行,每行表示一个操作,格式如题目描述所示。
Output
对于每个操作2,输出一行,每行一个数,表示询问的区间[l,r] 的魔法效果模100,000,007。
Sample Input
5
1 2 3 4 5
7
Q 1 3
M 2 7
Q 1 3
M 2 2
Q 1 3
M 4 2
Q 1 5
Sample Output
10
26
10
47
Data Constraint
30% 的数据:N,M<=300
另外20% 的数据:N,M<=30000, 操作1 的数量不超过50
80% 的数据:N,M<=30000
100% 的数据:N<=100000;M<=100000; 0<=ai, x<=1000
符合线段树的标准特征:动态地问你答案。
但是仔细看后我们会发现无从下手,因为没有办法合并区间。
由于 ai,x<=1000 ,我们会发现转成二进制以后最多只有十位,那我们就打十棵线段树吧!
考虑怎么合并:
我们把一段区间 [x..y] 分成 [x..m] 和 [m+1..y] ,那么 [x..y] 的所有连续子区间异或和就等于 [x..m] 的所有连续子区间异或和加上 [m+1]..y 的所有连续子区间异或和再加上穿过这两个区间的所有的连续子区间异或和。
那么问题的关键就在于如何求穿过两个区间的所有连续子区间和。
这时候需要想到异或的性质。
当1的个数为奇数时,异或出来的结果是1。当1的个数是偶数时,异或出来的结果是0。
我们在维护一段区间的所有的连续的子区间异或和的同时,多维护5个值,分别是该区间内1的个数的奇偶性,前缀异或和等于0,1的个数,后缀异或和等于0,1的个数。
那么穿过两个区间异或和为1的子区间的个数= [x..m] 后缀异或和等于0的个数× [m+1..y] 前缀异或和等于1的个数+ [x..m] 后缀异或和等于1的个数× [m+1..y] 前缀异或和等于0的个数。
至于怎么维护区间内前后缀异或和等于0,1的个数就用的到之前说要维护的1的个数的奇偶性了,请读者自行思考。
Code(pascal):
const rp=400005;
type arr=array[1..4] of int64;
var
i,j,k,n,m:longint;
ans,x,y:int64;
d:array[1..10,1..rp,1..4] of int64;
op:array[1..100000] of int64;
m2:array[0..10] of int64;
c:char;
procedure built;
begin
m2[0]:=1;
for i:=1 to 10 do m2[i]:=m2[i-1]*2;
end;
procedure maketree(t,i,x,y:int64);
var m:int64;
begin
if x=y then
begin
d[t,i,1]:=op[x] and m2[t-1] shr (t-1);
d[t,i,2]:=d[t,i,1]; d[t,i,3]:=d[t,i,1]; d[t,i,4]:=d[t,i,1];
end else
begin
m:=(x+y) div 2;
maketree(t,i+i,x,m); maketree(t,i+i+1,m+1,y);
d[t,i,1]:=d[t,i+i,1]+d[t,i+i+1,1]+d[t,i+i,3]*((y-m)-d[t,i+i+1,2])+((m-x+1)-d[t,i+i,3])*d[t,i+i+1,2];
if d[t,i+i,4]=0 then
begin
d[t,i,2]:=d[t,i+i+1,2]+d[t,i+i,2];
end else
begin
d[t,i,2]:=(y-m)-d[t,i+i+1,2]+d[t,i+i,2];
end;
if d[t,i+i+1,4]=1 then
begin
d[t,i,3]:=(m-x+1)-d[t,i+i,3]+d[t,i+i+1,3];
end else
begin
d[t,i,3]:=d[t,i+i,3]+d[t,i+i+1,3];
end;
d[t,i,4]:=(d[t,i+i,4]+d[t,i+i+1,4]) mod 2;
end;
end;
procedure change(t,i,x,y,l,r:int64);
var m:int64;
begin
if (x=l)and(y=l) then
begin
d[t,i,1]:=r and m2[t-1] shr (t-1);
d[t,i,2]:=d[t,i,1]; d[t,i,3]:=d[t,i,1]; d[t,i,4]:=d[t,i,1];
end else
begin
m:=(x+y) div 2;
if l<=m then change(t,i+i,x,m,l,r) else change(t,i+i+1,m+1,y,l,r);
d[t,i,1]:=d[t,i+i,1]+d[t,i+i+1,1]+d[t,i+i,3]*((y-m)-d[t,i+i+1,2])+((m-x+1)-d[t,i+i,3])*d[t,i+i+1,2];
if d[t,i+i,4]=0 then
begin
d[t,i,2]:=d[t,i+i+1,2]+d[t,i+i,2];
end else
begin
d[t,i,2]:=(y-m)-d[t,i+i+1,2]+d[t,i+i,2];
end;
if d[t,i+i+1,4]=1 then
begin
d[t,i,3]:=(m-x+1)-d[t,i+i,3]+d[t,i+i+1,3];
end else
begin
d[t,i,3]:=d[t,i+i,3]+d[t,i+i+1,3];
end;
d[t,i,4]:=(d[t,i+i,4]+d[t,i+i+1,4]) mod 2;
end;
end;
function find(t,i,x,y,l,r:int64):arr;
var
m:int64;
p,q:arr;
begin
if (x=l)and(y=r) then
begin
find:=d[t,i];
end else
begin
m:=(x+y) div 2;
if (r<=m) then
begin
find:=find(t,i+i,x,m,l,r);
end else
begin
if (l>m) then
begin
find:=find(t,i+i+1,m+1,y,l,r);
end else
begin
p:=find(t,i+i,x,m,l,m);
q:=find(t,i+i+1,m+1,y,m+1,r);
find[1]:=p[1]+q[1]+p[3]*((r-m)-q[2])+q[2]*((m-l+1)-p[3]);
if p[4]=0 then
begin
find[2]:=q[2]+p[2];
end else
begin
find[2]:=(r-m)-q[2]+p[2];
end;
if q[4]=1 then
begin
find[3]:=(m-l+1)-p[3]+q[3];
end else
begin
find[3]:=p[3]+q[3];
end;
find[4]:=(p[4]+q[4]) mod 2;
end;
end;
end;
end;
begin
built;
readln(n);
for i:=1 to n do read(op[i]);
for i:=1 to 10 do
maketree(i,1,1,n);
readln(m);
for i:=1 to m do
begin
readln(c,x,y);
if c='M' then
begin
for j:=1 to 10 do
change(j,1,1,n,x,y);
end else
begin
ans:=0;
for j:=1 to 10 do
ans:=(ans+find(j,1,1,n,x,y)[1]*m2[j-1]) mod 100000007;
writeln(ans);
end;
end;
end.
此法只能拿80分,因为常数太大。
0,1的个数可以只维护一个,这样常数会变小,还是比较危险(会打zkw非递归线段树的神牛勿喷)。
至于最好的做法,我们需要维护的是异或前缀和数组,这样修改就改成了区间修改,可是常数比较玄学,跑得非常快。
Code(pascal):
const
rp=800010;
var
d:array[1..10,1..rp] of int64;
b:array[1..10,1..rp] of int64;
op,oc:array[0..100000] of int64;
m2:array[0..9] of int64;
n,m,i,j:longint;
ans,k,x,y,l:int64;
c:char;
procedure maketree(t,i,x,y:int64);
var m:int64;
begin
if x=y then
begin
d[t,i]:=oc[x] and m2[t-1] shr (t-1);
end else
begin
m:=(x+y) div 2;
maketree(t,i+i,x,m); maketree(t,i+i+1,m+1,y);
d[t,i]:=d[t,i+i]+d[t,i+i+1];
end;
end;
procedure change(t,i,x,y,l,r:int64);
var m:int64;
begin
if (x=l)and(y=r) then
begin
d[t,i]:=(y-x+1)-d[t,i];
b[t,i]:=1-b[t,i];
end else
begin
m:=(x+y) div 2;
if b[t,i]=1 then
begin
d[t,i+i]:=(m-x+1)-d[t,i+i]; d[t,i+i+1]:=(y-m)-d[t,i+i+1];
b[t,i+i]:=1-b[t,i+i]; b[t,i+i+1]:=1-b[t,i+i+1];
b[t,i]:=0;
end;
if r<=m then
begin
change(t,i+i,x,m,l,r);
d[t,i]:=d[t,i+i]+d[t,i+i+1];
exit;
end;
if l>m then
begin
change(t,i+i+1,m+1,y,l,r);
d[t,i]:=d[t,i+i]+d[t,i+i+1];
exit;
end;
change(t,i+i,x,m,l,m); change(t,i+i+1,m+1,y,m+1,r);
d[t,i]:=d[t,i+i]+d[t,i+i+1];
end;
end;
function find(t,i,x,y,l,r:int64):int64;
var m:int64;
begin
if (x=l)and(y=r) then exit(d[t,i]);
m:=(x+y) div 2;
if b[t,i]=1 then
begin
d[t,i+i]:=(m-x+1)-d[t,i+i]; d[t,i+i+1]:=(y-m)-d[t,i+i+1];
b[t,i+i]:=1-b[t,i+i]; b[t,i+i+1]:=1-b[t,i+i+1];
b[t,i]:=0;
end;
if r<=m then exit(find(t,i+i,x,m,l,r));
if l>m then exit(find(t,i+i+1,m+1,y,l,r));
exit(find(t,i+i,x,m,l,m)+find(t,i+i+1,m+1,y,m+1,r));
end;
begin
m2[0]:=1; for i:=1 to 9 do m2[i]:=m2[i-1]*2;
readln(n);
for i:=1 to n do
begin
read(op[i]);
oc[i]:=op[i] xor oc[i-1];
end;
for i:=1 to 10 do maketree(i,1,1,n);
readln(m);
for i:=1 to m do
begin
readln(c,x,y);
if c='M' then
begin
for j:=1 to 10 do
if (y and m2[j-1] shr (j-1)) xor (op[x] and m2[j-1] shr (j-1))=1 then
begin
change(j,1,1,n,x,n);
end;
op[x]:=y;
end else
begin
ans:=0;
for j:=1 to 10 do
begin
if x>1 then k:=find(j,1,1,n,x-1,y) else k:=find(j,1,1,n,1,y);
l:=y-x-k+2;
ans:=(ans+l*k*m2[j-1]) mod 100000007;
end;
writeln(ans);
end;
end;
end.