这题时间卡的真紧,内存卡的更紧。。。
做这题时写了三份代码,
第一份是最基础的线段树区间更新,记录更新条件,开一颗线段树记录值,一颗记录每段区间的参考区间,一颗记录增加量,结果果断超时,数据强的一逼。
然后苦逼的空间换时间,开十棵线段树,分别记录对几取余,然后每次pushdown时需要哪个值就更新那个值,query时再全部更新,本以为过题目妥妥的,结果尼玛又超时,题目数据强的不止一逼。
最后再开大招,空间继续换时间,开了55棵线段树,这样一次只需要维护一颗,每次更新时不需要将之前的pushdown可以直接加上,55个20W的数组,尼玛第一次开这么大,最后空间险过,时间400多ms。
解释一下这题的解题思路:
线段树记录每个值的变化情况,另开一个数组记录初始情况。
对于所有取余情况,总共有55种,对1取余1种,对2取余2种。。。。然后对(i-l)%k=0,可变成i%k==l%k,这样对于一个区间,便可以根据其取余情况记录该区间应该更新哪一棵线段树,然后对这颗线段树这一区间的所有值都进行更新(延迟更新,查询时再更新),这样可能有人会问,对所有值都更新岂不不满足题意了?关键是,取结果的时候,位置确定,对1,对2,对3....的取余结果都能计算出来,也就是说,取值的时候取的线段树是确定的,不是所有的树都取,有些值虽然进行了更新,但是它可能永远都取不到,所以不会对结果造成影响。
具体参见代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define delf int m=(l+r)>>1
const int MAX=50010;
int sum[55][MAX<<2]; //55棵记录取余情况
int col[MAX<<2]; //记录是否更新过
int re[MAX]; //记录每个位置初始值
int lo[11]; //计算位置
void pushdown(int rt)
{
if (col[rt]==0)
return ;
col[rt]=0;
for (int i=0;i<55;i++) //要更新就要全部更新,才能使col变成0表示该节点已更新过,更新过或无需更新的节点sum=0,更新无影响
{
sum[i][rt<<1]+=sum[i][rt];
sum[i][rt<<1|1]+=sum[i][rt];
sum[i][rt]=0;
}
col[rt<<1]=col[rt<<1|1]=1;
return ;
}
void build(int l,int r,int rt) //初始化
{
col[rt]=0;
for (int i=0;i<55;i++)
sum[i][rt]=0;
if (l==r)
return ;
delf;
build(lson);
build(rson);
return ;
}
void update(int L,int R,int k,int c,int l,int r,int rt)
{
if (L<=l&&r<=R) //如果找到了满足题意的区间,便记录下来
{
int t=lo[k-1]+L%k; //计算应该更新哪一棵线段树,依据l%k==i%k
col[rt]=1;
sum[t][rt]+=c; //如果之前这个位置有值,直接加上即可,省去了一次pushdown
return ;
}
delf;
if (L<=m)
update(L,R,k,c,lson);
if (R>m)
update(L,R,k,c,rson);
return ;
}
int query(int k,int l,int r,int rt)
{
if (l==r) //找到查询位置
{
int s=0;
for (int i=1;i<=10;i++) //在55棵线段树中找有用的线段树
s+=sum[k%i+lo[i-1]][rt]; //有用的线段树的编号可以计算出来,然后计算所有更新情况的和
return s;
}
pushdown(rt);
delf;
if (m>=k)
return query(k,lson);
else
return query(k,rson);
}
int main()
{
int N;
lo[0]=0;
lo[1]=1;
for (int i=2;i<=10;i++) //计算线段树位置
lo[i]=i+lo[i-1];
while (~scanf("%d",&N))
{
build(1,N,1);
for (int i=1;i<=N;i++)
scanf("%d",&re[i]);
int q;
scanf("%d",&q);
int p,a,b,k,c;
for (int i=1;i<=q;i++)
{
scanf("%d",&p);
if (p==1)
{
scanf("%d%d%d%d",&a,&b,&k,&c);
update(a,b,k,c,1,N,1);
}
else
{
scanf("%d",&k);
int s=re[k];
s+=query(k,1,N,1); //加上初始值便是结果
printf("%d\n",s);
}
}
}
}