TYVJ 1941 - 「Poetize4」上帝造题的七分钟2

来源:互联网 发布:淘宝重复开店不查了 编辑:程序博客网 时间:2024/05/16 17:48

描述 Description

"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。

输入格式 InputFormat

第一行一个整数n,代表数列中数的个数。
第二行n个正整数,表示初始状态下数列中的数。
第三行一个整数m,表示有m次操作。
接下来m行每行三个整数k,l,r,k=0表示给[l,r]中的每个数开平方(下取整),k=1表示询问[l,r]中各个数的和。
UPD:注意数据中有可能l>r,所以遇到这种情况请交换l和r。

输出格式 OutputFormat

对于询问操作,每行输出一个回答。

样例输入 SampleInput

101 2 3 4 5 6 7 8 9 1050 1 101 1 101 1 50 5 81 4 8

样例输出 SampleOutput

1976

数据范围和注释 Hint

对于30%的数据,1<=n,m<=1000,数列中的数不超过32767。
对于100%的数据,1<=n,m<=100000,1<=l,r<=n,数列中的数大于0,且不超过1e12。
注意l有可能大于r,遇到这种情况请交换l,r。

题解

首先先讲暴力,有漏洞(l>r没特判),但有50分。

#include<cstdio>#include<cstring>#include<cstdlib>#include<iostream>#include<cmath>#define ll long longusing namespace std;int n,m;ll a[100002];void init(){scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%lld",&a[i]);scanf("%d",&m);}void doit(){int k,l,r;ll ans=0;scanf("%d%d%d",&k,&l,&r);if(k==0)   {for(int i=l;i<=r;i++) a[i]=(ll) sqrt(a[i]);}else   {for(int i=l;i<=r;i++) ans+=a[i];    printf("%lld\n",ans);   }}int main(){init(); while(m--) doit();return 0;}

接下来正解:树状数组(前缀和),并查集优化。

从暴力可以看出,主要操作中求和可能优化,所以我们想到的是前缀和,可单纯的前缀和数组无法完成开根号。所以我们可以考虑用线段树或树状数组,每次暴力修改叶节点。

并且,数字一旦开到 1就不用再处理了。因为答案都是1。所以用并查集优化,将已经变为1的点跳过。这是对修改的优化。

#include<cstdio>#include<cstring>#include<cstdlib>#include<iostream>#include<cmath>#include<algorithm>#define ll long longusing namespace std;int n,m,fa[100002];ll a[100002],tr[100002];int lowbit(int x) {return x&(-x);}void insert(int w,ll x){for(int i=w;i<=n;i+=lowbit(i)) tr[i]+=x;}ll ask(int x){ll ans=0;for(int i=x;i>0;i-=lowbit(i)) ans+=tr[i];return ans;}void init(){scanf("%d",&n);for(int i=1;i<=n;i++)   {scanf("%lld",&a[i]);    fa[i]=i; insert(i,a[i]);   }fa[n+1]=n+1;scanf("%d",&m);}int find(int x){if(fa[x]!=x) fa[x]=find(fa[x]);return fa[x];}void doit(){int k,l,r;scanf("%d%d%d",&k,&l,&r);if(l>r) swap(l,r);if(k==0)   {for(int i=find(l);i<=r;i=find(i+1))       {ll t=(ll) sqrt(a[i]);    insert(i,t-a[i]);//用树状数组求和     a[i]=t;    if(a[i]==1) fa[i]=i+1;//当一个点的值为1时,直接跳过这个点    }   }else   printf("%lld\n",ask(r)-ask(l-1));}int main(){init(); while(m--) doit();return 0;}



0 0
原创粉丝点击