北航校赛的一道题。。。
看到数据量就直接想到线段树实现了,但是区间修改的操作不是线性关系,而是对区间内的每个数进行平方,那就需要对线段树的节点进行类似于一种状态压缩的处理了。
因为结果是要对61求余的,所以一开始想到的就是每个节点保存60个值,分别代表对应数字在改区间内出现的次数,虽然空间复杂度勉强过的去,但是时间复杂度就难看了。。。。(囧,一个节点要计算60次跪了)然后就想到优化一下,如果该节点的区间范围小于61,就直接在此截止,下面直接就是个线性表,相当于线段树砍掉下面的一部分,补了个线性表,那么原来的线段树层数就变成log2(100000/61)≈11层,加一层线性表那么一次操作就是12*60=720次,乘个100000就是7200w,虽然题目时限8000ms,但这只是一颗10w节点的树的处理时间。。。所以还是跪。。。
然后就寻思着开始找规律,直接把1~60所有的值都循环处理一遍,其中存在4个环,3个环的长度为4,1个环的长度为2,(看了别人的题解才想到费马小定理可以直接得到6次平方操作==2次平方操作的结论【一口老血都喷在屏幕上】,数论没学好=.=)那就是任意一个数进行2次平方后就会进入一个4长度或者2长度的循环节中,那么对于每个节点只要保存6种情况(2种+4种(循环)的sum就可以了,对于较大的延迟标记lazy的处理是先减一个2再对循环节4求余,然后把2加回来,这样lazy就减小了。于是乎。。更新的问题就解决了。
#include <stdio.h>
const int Mod = 61;
const int MAXM = 110000;
const int Size = 6;
int L,R;
struct P
{
int Now[MAXM<<2];
int Lazy[MAXM<<2];
int Sum[MAXM<<2][Size];
void Pushup(int pos)
{
int a=Now[pos<<1],b=Now[pos<<1|1];
Now[pos]=0;
for(int i=0;i<Size;i++)
{
Sum[pos][i]=Sum[pos<<1][a]+Sum[pos<<1|1][b];
a++;b++;
if(a==Size)a=2;
if(b==Size)b=2;
}
}
void Pushdown(int pos)
{
if(Lazy[pos])
{
Lazy[pos<<1]+=Lazy[pos];
Lazy[pos<<1|1]+=Lazy[pos];
if(Lazy[pos]>=2)Lazy[pos]=(Lazy[pos]-2&3)+2;
while(Lazy[pos])
{
Now[pos<<1|1]++;
Now[pos<<1]++;
if(Now[pos<<1|1]==Size)Now[pos<<1|1]=2;
if(Now[pos<<1]==Size)Now[pos<<1]=2;
Lazy[pos]--;
}
}
}
void Build(int Left,int Right,int pos)
{
Lazy[pos]=0;
if(Left==Right)
{
scanf("%d",Sum[pos]);
*Sum[pos]%=Mod;
for(int i=1;i<Size;i++)
{
Sum[pos][i]=Sum[pos][i-1]*Sum[pos][i-1]%Mod;
}
Now[pos]=0;
return;
}
int Mid=(Left+Right)>>1;
Build(Left,Mid,pos<<1);
Build(Mid+1,Right,pos<<1|1);
Pushup(pos);
}
int Query(int Left,int Right,int pos)
{
if(L<=Left&&Right<=R)return Sum[pos][Now[pos]];
Pushdown(pos);
int Mid=(Left+Right)>>1;
int ans=0;
if(L<=Mid)ans+=Query(Left,Mid,pos<<1);
if(R>Mid)ans+=Query(Mid+1,Right,pos<<1|1);
return ans;
}
void Updata(int Left,int Right,int pos)
{
if(L<=Left&&Right<=R)
{
Now[pos]++;
if(Now[pos]==Size)Now[pos]=2;
Lazy[pos]++;
return;
}
Pushdown(pos);
int Mid=(Left+Right)>>1;
if(L<=Mid)Updata(Left,Mid,pos<<1);
if(R>Mid)Updata(Mid+1,Right,pos<<1|1);
Pushup(pos);
}
}Tree;
int main()
{
int Ans;
char Com;
int Num,Quest;
while(~scanf("%d %d",&Num,&Quest))
{
Tree.Build(1,Num,1);
while(Quest--)
{
scanf(" %c %d %d",&Com,&L,&R);
if(Com=='Q')
{
Ans=Tree.Query(1,Num,1)%Mod;
printf("%d\n",Ans*Ans%Mod);
}
else
{
Tree.Updata(1,Num,1);
}
}
}
}
中间把&运算的优先级记错了WA了好几发。。 卧槽这游戏简直难玩
本文介绍了一道北航校赛中的线段树优化题解过程,通过状态压缩减少节点信息存储数量,并利用费马小定理简化平方操作,最终实现高效区间查询与更新。

1万+

被折叠的 条评论
为什么被折叠?



