P4145
上帝造题的七分钟 2 / 花神游历各国
似乎考验了乱搞的能力。
首先,
也就是说,每个点被修改后会发生变化的修改次数是极有限的。
那么我们感一件非常暴力的事情,建立一个并查集,一个树状数组,一开始暴力修改点,修改后为 1 或 0 的我们以后就不用再修改这个点了,那么我们把它与下一个点用并查集合并,下次修改再遇到的时候直接通过并查集跳到其祖先即可。
时间复杂度
当然,利用这个思路也可以用线段树,全 1 或 0 的区间打上标记,以后遇到不再修改,同样也可以达到效果,不过就显得常规了。
代码(树状数组):
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const ll N=1e6;
ll n,m,op,l,r,x;
ll fa[N+5],a[N+5],c[N+5];
void add(ll x,ll y) {
for(;x<=n;x+=x&-x) c[x]+=y;
}
ll ask(ll x) {
ll res=0;
for(;x;x-=x&-x) res+=c[x];
return res;
}
ll find(ll x) {
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void uni(ll x,ll y) {
fa[find(x)]=find(y);
}
inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}
void write(ll x) {
if(x<0) {x=-x;putchar('-');}
ll y=10,len=1;
while(y<=x) {y*=10;len++;}
while(len--) {y/=10;putchar(x/y+48);x%=y;}
}
int main() {
n=read();
for(ll i=1;i<=n;i++) {
a[i]=read();add(i,a[i]);
fa[i]=i;
}
m=read();
while(m--) {
op=read();l=read();r=read();
if(l>r) swap(l,r);
if(op==0) {
x=l;
while(x<=r) {
// printf("test: x=%lld\n",x);
while(a[x]<=1) {
uni(x,x+1);x=fa[x];
}
if(x>r) break;
add(x,-a[x]);a[x]=(ll)sqrt(a[x]);
add(x,a[x]);x++;
}
}
if(op==1) {
write(ask(r)-ask(l-1));
putchar('\n');
}
// printf("test:\n");
// for(ll i=1;i<=n;i++) {
// printf("%lld ",ask(i)-ask(i-1));
// }
// printf("\n");
}
return 0;
}