K-Master of Graph 长春理工大学第十四届程序设计竞赛
题目链接:题目传送门
题意:点击上面链接看原题
主要思路:
使用线段树,树中每个节点都保存这个区间内所有数的和。然后后来的操作就是区间更新和区间查询了。
但是如果只是这样的话会超时,还要做一些优化。想一想,如果一个数本身就是一位数(小于10),那么它的各个数位上的数之和就等一它本身,所以这种情况就不需要再更新了。所以在节点中增加一个变量用来保存本区间内的最大值,在更新的时候判断一下区间中的最大值是否小于10,如果小于10就不需要更新。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e5+100;
struct N
{
int l,r;
ll sum,ma;
}tree[M<<2];
ll a[M];
ll f(ll x)
{
ll ans=0;
while(x)
{
ans+=(x%10);
x/=10;
}
return ans;
}
void build(int i,int l,int r)
{
tree[i].l=l;tree[i].r=r;
if(l==r)
{
tree[i].sum=tree[i].ma=a[l];
return;
}
int mid=(l+r)/2;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
tree[i].ma=max(tree[i*2].ma,tree[i*2+1].ma);
}
void update(int i,int l,int r)
{
if(tree[i].ma<10)return ; //如果区间中的最大值小于10,则不用更新
if(tree[i].l==l&&tree[i].r==r&&l==r)
{
tree[i].sum=tree[i].ma=f(tree[i].sum);
return ;
}
int mid=(tree[i].l+tree[i].r)/2;
if(r<=mid)update(i*2,l,r);
else if(mid<l)update(i*2+1,l,r);
else
{
update(i*2,l,mid);
update(i*2+1,mid+1,r);
}
tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
tree[i].ma=max(tree[i*2].ma,tree[i*2+1].ma);
}
ll query(int i,int l,int r)
{
if(tree[i].l==l&&tree[i].r==r)return tree[i].sum;
int mid=(tree[i].l+tree[i].r)/2;
if(r<=mid)return query(i*2,l,r);
else if(mid<l)return query(i*2+1,l,r);
else return query(i*2,l,mid)+query(i*2+1,mid+1,r);
}
int main()
{
freopen("in.txt","r",stdin);
int n,m,q,u,v,op;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",a+i);
build(1,1,n);
for(int i=0;i<m;i++)
scanf("%d%d",&u,&v);
scanf("%d",&q);
while(q--)
{
scanf("%d%d%d",&op,&u,&v);
if(op)
{
update(1,u,v);
}
else
{
printf("%lld\n",query(1,u,v));
}
}
return 0;
}