知识点:线段树(区间查询+区间修改)
这道题不同于以往的增减或乘除,增减乘除都是有结合律的,但是开根号是木有的(可能是我孤陋寡闻),所以只能单点修改,但是的话它又是有一定规律的,所以可以类似剪枝般省去一些步骤。
说明一下,这题并不难,但是非常非常坑qwd,坑点画红在下面
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
ll n,m,num[200000],tree[1800500],top=0;(常规操作)
class lktr{(打包好放一起,如果直接在类中定义数组会爆,只能开很小QAQ,所以放外面了)
public:
void bd_tr(ll r=1,ll srt=1,ll edi=n){
if(srt==edi){
tree[r]=num[srt];
return ;
}
ll lf_r=2*r;
ll rg_r=2*r+1;
ll mid=(srt+edi)/2;
bd_tr(lf_r,srt,mid);
bd_tr(rg_r,mid+1,edi);
tree[r]=tree[lf_r]+tree[rg_r];
}
void update(ll r,ll srt,ll edi,ll lf,ll rg){
if(tree[r]==(edi-srt+1)||srt>rg||edi<lf)return ;(如果它的覆盖长度恰好等于区间和,说明这段区间全部一定是1(因为都大于0嘛))
if(srt==edi) {
tree[r]=(ll)sqrt(tree[r]*1.0);
return ;
}
ll lf_r=2*r;
ll rg_r=2*r+1;
ll mid=(srt+edi)/2;
if(lf<=mid)update(lf_r,srt,mid,lf,rg);
if(mid<rg)update(rg_r,mid+1,edi,lf,rg);
tree[r]=tree[lf_r]+tree[rg_r];
}
ll query(ll r,ll srt,ll edi,ll lf,ll rg){
if(srt>rg||edi<lf)return 0;
if(lf<=srt&&edi<=rg)return tree[r];
ll lf_r=2*r;
ll rg_r=2*r+1;
ll mid=(srt+edi)/2;
ll sum=0;
if(lf<=mid)sum+=query(lf_r,srt,mid,lf,rg);
if(mid<rg)sum+=query(rg_r,mid+1,edi,lf,rg);
return sum;
}
void init(void){(初始化,多组数据)
memset(tree,0,sizeof(tree));
memset(num,0,sizeof(num));
top=0;
}
};
int main(){
ll tot=0;
lktr tr;
while(scanf("%lld",&n)!=EOF){(这个是输入一直到文件结束)
ll i,t,x,y;
tot++;
tr.init();
for(i=1;i<=n;i++)
scanf("%lld",&num[i]);
scanf("%lld",&m);
tr.bd_tr();
printf("Case #%lld:\n",tot);
for(i=1;i<=m;i++){
scanf("%lld%lld%lld",&t,&x,&y);
if(x>y)swap(x,y);(x不一定小于y,要自己判断)
if(t==0)tr.update(1,1,n,x,y);
else printf("%lld\n",tr.query(1,1,n,x,y));
}
printf("\n");(数据处理完后记得再输出个\n)
}
}(这道题的全部数据最好都用longlong不然容易见祖宗捏QAQ)
总结:一道比较坑的题目