题解、Writeup、游记和碎碎念
XLk 觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。 "第一分钟,X 说,要有数列,于是便给定了一个正整数数列。 第二分钟,L 说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。 第三分钟,k 说,要能查询,于是便有了求一段数的和的操作。 第四分钟,彩虹喵说,要是 noip 难度,于是便有了数据范围。 第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。 第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过 64 位有符号整数类型的表示范围的限制。 第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。" ——《上帝造题的七分钟·第二部》 所以这个神圣的任务就交给你了。
第一行一个整数 n,代表数列中数的个数。 第二行 n 个正整数,表示初始状态下数列中的数。 第三行一个整数 m,表示有 m 次操作。 接下来 m 行每行三个整数 k,l,r,k=0 表示给[l,r]中的每个数开平方(下取整),k=1 表示询问[l,r]中各个数的和。
对于询问操作,每行输出一个回答。
10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8
19
7
6
显然,一个数最多开根 6 次就会变成 1,那么直接用线段树维护区间最大值,每次暴力修改即可。
#include<bits/stdc++.h>
typedef unsigned char uchar;
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
#define xx first
#define yy second
template<typename T> inline T max(T a,T b){return a>b?a:b;}
template<typename T> inline T min(T a,T b){return a<b?a:b;}
template<typename T> inline T abs(T a){return a>0?a:-a;}
template<typename T> inline void repr(T &a,T b){if(a<b)a=b;}
template<typename T> inline void repl(T &a,T b){if(a>b)a=b;}
#define mp(a,b) std::make_pair(a,b)
#define pb push_back
int n,t=1;
ll s[270000],ma[270000];
inline void up(int x)
{
s[x]=s[x<<1]+s[x<<1|1];
ma[x]=max(ma[x<<1],ma[x<<1|1]);
}
void modify(int x,int l,int r,int ql,int qr)
{
if(ma[x]<=1)return;
if(x&t)
{
ma[x]=sqrt(ma[x]);
s[x]=ma[x];
return;
}
int q=(l+r)>>1;
if(ql<q)modify(x<<1,l,q,ql,min(q,qr));
if(qr>q)modify(x<<1|1,q,r,max(q,ql),qr);
up(x);
}
int main()
{
int m;
scanf("%d",&n);
while(t<n+2)t<<=1;
for(int i=1;i<=n;i++)
scanf("%lld",ma+i+t),s[i+t]=ma[i+t];
for(int i=t-1;i;i--)
up(i);
scanf("%d",&m);
while(m--)
{
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
if(x>y)std::swap(x,y);
if(opt==0)
{
modify(1,1,t,x,y+1);
}
else
{
x=x+t-1,y=y+t+1;
ll ans=0;
while(x^y^1)
{
if(~x&1)ans+=s[x^1];
if(y&1)ans+=s[y^1];
x>>=1,y>>=1;
}
printf("%lld\n",ans);
}
}
}
日期: 2016-11-09
这是一篇旧文,原始文章及评论可在 https://oldblog.mcfx.us/archives/70/ 查看。