P2667防御分块做法
WrongAnswer_90 · · 题解
Page 0
为什么都没有分块的题解啊。
题目感觉难度不是绿题,建议升蓝。
Page 1
看到这题,我首先想到的是线段树,然后感觉不可做。
我果然太菜了
一看数据范围:
这道题最大的问题是攻击了一次打了大标记后可能有一些盾碎掉了,下一次应该造成双倍伤害,但是却没有及时标记。
考虑维护块内最小值(下文记作
如果
有些人可能会觉得时间复杂度不对,但是其实是正确的。
因为每一个盾只会碎一次,所以每一个盾的复杂度加成最多是
对于散块,注意更改了之后维护
然后就是基本操作了。
注意,假如盾的血量是
这道题开了 long long 之后好像不需要中间取模,最后再取模就可以。
具体实现细节见代码:
码风略丑,勿喷
#include<iostream>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
//a 是盾的血量,tg 是伤害标记, minn 是块内最小值,b是答案
int a[1000001],tg[1000001],all,minn[10001],b[1000001],x,t,n,m,l,r,k;
inline int read()
{
int ans=0;char ch=getchar();
while((ch>'9')||(ch<'0'))ch=getchar();
while((ch>='0')&&(ch<='9'))ans=ans*10+ch-'0',ch=getchar();
return ans;
}
char opt;
signed main()
{
n=read();m=read();t=sqrt(n);
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)if(a[i]==0)a[i]=9999999999999999;
for(int i=1;i<=n;i+=t)minn[(i-1)/t+1]=9999999999999999;
for(int i=1;i<=n;i++)minn[(i-1)/t+1]=min(minn[(i-1)/t+1],a[i]);
//不要忘记初始化,a一开始可能就是0
while(m--)
{
cin>>opt;
if(opt=='Q')
{
x=read();
if(a[x]==9999999999999999)all=(all+(b[x]+tg[(x-1)/t+1]*2)%1000000009)%1000000009;
else all=(all+(b[x]+tg[(x-1)/t+1])%1000000009)%1000000009;
//cout<<all<<endl;
}
else
{
l=read();r=read();x=read();
if((l-1)/t+1==(r-1)/t+1)//如果在同一块内
{
bool minf=0;//minf 表示当前块是否需要重构 ,以下同上
for(int i=l;i<=r;i++)
{
if(a[i]==9999999999999999)b[i]+=2*x;//如果盾已经碎了
else b[i]+=x;
if(a[i]!=9999999999999999)
a[i]-=x;
minn[(l-1)/t+1]=min(minn[(l-1)/t+1],a[i]);//不要忘记维护 minn
if(a[i]<=tg[(l-1)/t+1])minf=1;
}
if(minf)
{
minn[(l-1)/t+1]=9999999999999999;
for(int i=(l-1)/t*t+1;i<=min((l-1)/t*t+t,n);i++)
{
if(a[i]==9999999999999999)b[i]+=2*tg[(l-1)/t+1];
else b[i]+=tg[(l-1)/t+1];
if(a[i]!=9999999999999999)
a[i]-=tg[(l-1)/t+1];
if(a[i]<=0)a[i]=9999999999999999;
minn[(l-1)/t+1]=min(minn[(l-1)/t+1],a[i]);
}
tg[(l-1)/t+1]=0;//tag 不要忘记清空
}
}
else
{
//处理左边散块
bool minf=0;
for(int i=l;i<=min(((l-1)/t+1)*t,n);i++)
{
if(a[i]==9999999999999999)b[i]+=x*2;
else b[i]+=x;
if(a[i]!=9999999999999999)
a[i]-=x;
minn[(l-1)/t+1]=min(minn[(l-1)/t+1],a[i]);
if(a[i]<=tg[(l-1)/t+1])minf=1;
}
if(minf)
{
minn[(l-1)/t+1]=9999999999999999;
for(int i=(l-1)/t*t+1;i<=min(((l-1)/t+1)*t,n);i++)
{
if(a[i]==9999999999999999)b[i]+=tg[(l-1)/t+1]*2;
else b[i]+=tg[(l-1)/t+1];
if(a[i]!=9999999999999999)
a[i]-=tg[(l-1)/t+1];
if(a[i]<=0)a[i]=9999999999999999;
minn[(l-1)/t+1]=min(minn[(l-1)/t+1],a[i]);
}
tg[(l-1)/t+1]=0;
}
//中间整块
for(int i=(l-1)/t+2;i<(r-1)/t+1;i++)
{
tg[i]+=x;
if(tg[i]>=minn[i])
{
minn[i]=9999999999999999;
for(int j=(i-1)*t+1;j<=min((i-1)*t+t,n);j++)
{
if(a[j]==9999999999999999)b[j]+=tg[i]*2;
else b[j]+=tg[i];
if(a[j]!=9999999999999999)
a[j]-=tg[i];
if(a[j]<=0)a[j]=9999999999999999;
minn[i]=min(minn[i],a[j]);
}
tg[i]=0;
}
}
minf=0;
//右边散块
for(int i=(r-1)/t*t+1;i<=r;i++)
{
if(a[i]==9999999999999999)b[i]+=x*2;
else b[i]+=x;
if(a[i]!=9999999999999999)
a[i]-=x;
minn[(r-1)/t+1]=min(minn[(r-1)/t+1],a[i]);
if(a[i]<=tg[(r-1)/t+1])minf=1;
}
if(minf)
{
minn[(r-1)/t+1]=9999999999999999;
for(int i=(r-1)/t*t+1;i<=min((r-1)/t*t+t,n);i++)
{
if(a[i]==9999999999999999)b[i]+=tg[(r-1)/t+1]*2;
else b[i]+=tg[(r-1)/t+1];
if(a[i]!=9999999999999999)
a[i]-=tg[(r-1)/t+1];
if(a[i]<=0)a[i]=9999999999999999;
minn[(r-1)/t+1]=min(minn[(r-1)/t+1],a[i]);
}
tg[(r-1)/t+1]=0;
}
}
}
}
printf("%lld",all%1000000009);
return 0;
}