惊恐!分块竟比线段树快
查看原帖
惊恐!分块竟比线段树快
85994
嘉年华楼主2021/7/28 15:42

搜分块进来,于是就打了分块,结果卡了一下变成了170ms?!
code在下面,不知道是不是少考虑了什么,求dalao康康

#include<bits/stdc++.h>
using namespace std;

#define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?EOF:*S++)
namespace get_out
{
	char B[1<<15],*S=B,*T=B;
	char op;
	inline void read_(int &x)
	{
		x=0;
		int fi(1);
		op=getchar();
		while((op<'0'||op>'9')&&op!='-') op=getchar();
		if(op=='-') op=getchar(),fi=-1;
		while(op>='0'&&op<='9') x=(x<<1)+(x<<3)+(op^48),op=getchar();
		x*=fi;
		return;
	}
	inline void read_(long long &x)
	{
		x=0;
		int fi(1);
		op=getchar();
		while((op<'0'||op>'9')&&op!='-') op=getchar();
		if(op=='-') op=getchar(),fi=-1;
		while(op>='0'&&op<='9') x=(x<<1)+(x<<3)+(op^48),op=getchar();
		x*=fi;
		return;
	}
	inline void read_(double &x)
	{
		x=0.0;
		float fi(1.0),sm(0.0);
		op=getchar();
		while((op<'0'||op>'9')&&op!='-') op=getchar();
		if(op=='-') op=getchar(),fi=-1.0;
		while(op>='0'&&op<='9') x=(x*10.0)+(op^48),op=getchar();
		if(op=='.') op=getchar();
		while(op>='0'&&op<='9') sm=(sm*10.0)+(op^48),op=getchar();
		while(sm>1.0) sm/=10.0;
		x+=sm,x*=fi;
		return;
	}
	inline void write_(int x)
	{
		if(x<0) x=-x,putchar('-');
		if(x>9) write_(x/10);
		putchar(x%10+'0');
	}
	inline void write_(long long x)
	{
		if(x<0) x=-x,putchar('-');
		if(x>9) write_(x/10);
		putchar(x%10+'0');
	}
}
using namespace get_out;
//读写优化 
#define maxn 200005
#define mod 998244353

class a_block
{
	public:
		int maxx;//区间最大值 
		int cnt;//这个区间有多少次平方操作 
		int l,r;//左界,右界 
};
a_block block[2000];//块 
int tot;//块数 

int n,m;//
int block_len;//块长 
int a[maxn],of[maxn];//值,所属的块 
int cnt[maxn];//单独计数每个点除区间外的平方次数(两边总得暴力) 

inline void renew(int pos)
{
	block[pos].maxx=0;
	for(int j(block[pos].l);j<=block[pos].r;++j)
		if(block[pos].maxx<a[j])
			block[pos].maxx=a[j];
}//暴力刷新区间最大值 

inline void downput(int pos)
{
	--block[pos].cnt;
	for(int j(block[pos].l);j<=block[pos].r;++j)
		++cnt[j];
}//开方时可能需要下放标记的平方次数,暴力下放 

inline void solve(void)
{
	for(register int i(1),l,r,k,posl,posr;i<=m;++i)
	{
		read_(k),read_(l),read_(r);
		posl=of[l],posr=of[r];
		if(k==1)//开方 
		{
			if(posl==posr)//同一区间 
			{
				if(block[posl].cnt==0)
					for(int j(l);j<=r;++j)
						if(cnt[j])
							--cnt[j];
						else
							a[j]=floor(sqrt(a[j]));
				else
				{
					--block[posl].cnt;
					for(int j(block[posl].l);j<l;++j)
						++cnt[j];
					for(int j(r+1);j<=block[posl].r;++j)
						++cnt[j];
				}//要下放标记,因为是不可逆的(有取整) 
				renew(posl);//开方后重算最大值 
			}
			else
			{
				bool ifl(0),ifr(0);//是否需要重置最大值 
				if(block[posl].maxx>1)//容易知道最大值小于等于1时任何操作没有意义 
				{
					if(block[posl].cnt)
						downput(posl);
					for(int j(l);j<=block[posl].r;++j)
						if(a[j]>1)
							if(cnt[j])
								--cnt[j];
							else
								a[j]=floor(sqrt(a[j])),ifl=1;
				}
				if(block[posr].maxx>1)
				{
					if(block[posr].cnt)
						downput(posr);
					for(int j(block[posr].l);j<=r;++j)
						if(a[j]>1)
							if(cnt[j])
								--cnt[j];
							else
								a[j]=floor(sqrt(a[j])),ifr=1;
				}
				for(int j(posl+1);j<posr;++j)
					if(block[j].maxx>1)
					{
						if(block[j].cnt)
							--block[j].cnt;//使用标记 
						else//没有标记就暴力开方 
						{
							for(int k(block[j].l);k<=block[j].r;++k)
								if(cnt[k])
									--cnt[k];
								else
									a[k]=floor(sqrt(a[k]));
							renew(j);//开方后重算最大值 
						}
					}
				if(ifl) renew(posl);//开方后重算最大值 
				if(ifr) renew(posr);//开方后重算最大值 
			}
		}
		else
		{
			if(posl==posr)
			{
				for(int j(l);j<=r;++j)
					++cnt[j];
				//renew(posl);
				//只有开方会影响最大值,此时的cnt虽然可能改变maxx的值,但我们只关心最大值是否大于1,所以是正确的 
			}
			else
			{
				if(block[posl].maxx>1)
    				for(int j(l);j<=block[posl].r;++j)
    					++cnt[j];
    			if(block[posr].maxx>1)
    				for(int j(block[posr].l);j<=r;++j)
    					++cnt[j];
				for(int j(posl+1);j<posr;++j)
					if(block[j].maxx>1)
						++block[j].cnt;
				//renew(posl),renew(posr);
			}
		}
	}
}

#define LL long long

inline LL fast_(LL b,LL p,LL MOD_LL)
{
	LL c=b,ans=1LL;
	while(p)
	{
		if(p&1) ans*=c%MOD_LL,ans%=MOD_LL;
		c*=c,c%=MOD_LL,p>>=1;
	}
	return ans;
}//取模快速幂 

signed main()
{
	read_(n),read_(m);
	block_len=pow(n,0.45)+10;//块长 
	const double comp=log(mod-1),xs=log(2);
	for(register int i(1);i<=n;++i)
		read_(a[i]);
	//读入 
	for(int i(1);i<=n;i+=block_len)
	{
		block[++tot].l=i,block[tot].r=min(i+block_len-1,n);
		for(int j(i);j<=block[tot].r;++j)
		{
			of[j]=tot;
			if(block[tot].maxx<a[j])
				block[tot].maxx=a[j];
		}
	}//分块 
	solve();//处理 
	LL ans(0);
	for(register int i(1);i<=n;++i)
		if(xs*(cnt[i]+block[of[i]].cnt)<comp)
			ans=(ans+fast_(a[i],fast_(2,(LL)(cnt[i]+block[of[i]].cnt),mod-1),mod))%mod;
		else
			ans=(ans+fast_(a[i],mod-1+fast_(2,(LL)(cnt[i]+block[of[i]].cnt),mod-1),mod))%mod;
	//拓展欧拉定理算最后结果 
	write_(ans),putchar('\n');
    return 0;
}
2021/7/28 15:42
加载中...