BUAA 1088 再也不会依赖任何人了

本文介绍了一道北航校赛中的线段树优化题解过程,通过状态压缩减少节点信息存储数量,并利用费马小定理简化平方操作,最终实现高效区间查询与更新。


北航校赛的一道题。。。

看到数据量就直接想到线段树实现了,但是区间修改的操作不是线性关系,而是对区间内的每个数进行平方,那就需要对线段树的节点进行类似于一种状态压缩的处理了。

因为结果是要对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了好几发。。   卧槽这游戏简直难玩

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值