BZOJ3211 花神游历各国(树状数组+并查集)

【题解】

查询很容易做到O(logN)
修改时注意:
第i位上的数a[i] -> sqrt(a[i]),最多执行30次,当a[i]==1后,可以不修改,直接跳过 
跳到之后第一个大于1的位置上去,这个位置可以用并查集维护 
由于每个点最被修改30次,修改的总复杂度为O(300*N)

注意分析题目中修改操作的性质 

注意两个小优化:1. fa[n+1]=n+1

                                2. if(a[i]>1) fa[i]=i;else fa[i]=i+1;

不知道为什么,这两个去掉哪一个都会TLE很久


【代码】

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
typedef long long LL;
LL a[100005],c[100005];
int fa[100005];
int n;
int father(int x)//father(i):从i起第一个大于1的数的位置 
{
	if(fa[x]!=x) fa[x]=father(fa[x]);
	return fa[x];
}
void xg(int p,int i)
{
	for(;i<=n;i+=i&(-i))
		c[i]+=p;
}
LL cx(int i)
{
	LL sum=0;
	for(;i>0;i-=i&(-i))
		sum+=c[i];
	return sum;
}
int main()
{
	int i,m,x,l,r;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		xg(a[i],i);
		if(a[i]>1) fa[i]=i;
		else fa[i]=i+1;
	}
	fa[n+1]=n+1;//注意 
	scanf("%d",&m);
	for(;m>0;m--)
	{
		scanf("%d%d%d",&x,&l,&r);
		if(x==1) printf("%lld\n",cx(r)-cx(l-1));//查询 
		else//修改 
		{
			for(i=father(l);i<=r;i=father(i+1))
			{
				xg((LL)sqrt((double)a[i])-a[i],i);
				a[i]=(LL)sqrt((double)a[i]);
				if(a[i]==1) fa[i]=father(i+1);
			}
		}
	}
	return 0;
}


你可能感兴趣的:(并查集,树状数组,开平方)