首页 > 学院 > 开发设计 > 正文

[SD2014集训]查询(分块+数学相关)

2019-11-14 10:42:34
字体:
来源:转载
供稿:网友

题目描述

这里写图片描述 这里写图片描述 这里写图片描述 这里写图片描述

题解

看模数那么奇怪找一下规律看看有没有奇怪的性质 发现每一个数立方48次后回到原数 线段树不如分块好写 维护每个数立方k次后得到的数,每一个块所有的数分别立方k次后的和 修改时,对于整块记录立方的次数,其余的暴力重构 重构即把块内的点维护的立方旋转t次,然后重新计算和 查询时,整块直接查询,其余暴力 时间复杂度O(m(block+n∗szblock)),可以发现当block=n∗sz−−−−−√时有最小值,O(mn∗sz−−−−−√)≈2.19s

代码

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>using namespace std;#define LL long long#define Mod 329701061#define sz 48#define N 100005char opt;int n,m,x,y,block,tot;int t[N],num[N],l[N],r[N];LL a[N],cub[N][sz],Cub[N][sz],s[sz];void init(){ for (int i=1;i<=n;++i) { cub[i][0]=a[i]; for (int j=1;j<sz;++j) cub[i][j]=cub[i][j-1]*cub[i][j-1]%Mod*cub[i][j-1]%Mod; } for (int i=1;i<=tot;++i) for (int j=0;j<sz;++j) for (int k=l[i];k<=r[i];++k) { Cub[i][j]+=cub[k][j]; if (Cub[i][j]>=Mod) Cub[i][j]-=Mod; }}void move(int id,int rad){ if (!rad) return; for (int i=0;i<rad;++i) s[i]=cub[id][i]; for (int i=rad;i<sz;++i) cub[id][i-rad]=cub[id][i]; for (int i=0;i<rad;++i) cub[id][i+sz-rad]=s[i];}void calc(int id){ for (int i=0;i<sz;++i) { Cub[id][i]=0; for (int j=l[id];j<=r[id];++j) { Cub[id][i]+=cub[j][i]; if (Cub[id][i]>=Mod) Cub[id][i]-=Mod; } }}void change(int x,int y){ if (num[x]==num[y]) { for (int i=l[num[x]];i<x;++i) move(i,t[num[x]]); for (int i=y+1;i<=r[num[y]];++i) move(i,t[num[x]]); ++t[num[x]];if (t[num[x]]==sz) t[num[x]]=0; for (int i=x;i<=y;++i) move(i,t[num[x]]); t[num[x]]=0; calc(num[x]); return; } if (x==l[num[x]]) x=num[x]; else { for (int i=l[num[x]];i<x;++i) move(i,t[num[x]]); ++t[num[x]];if (t[num[x]]==sz) t[num[x]]=0; for (int i=x;i<=r[num[x]];++i) move(i,t[num[x]]); t[num[x]]=0; calc(num[x]); x=num[x]+1; } if (y==r[num[y]]) y=num[y]; else { for (int i=y+1;i<=r[num[y]];++i) move(i,t[num[y]]); ++t[num[y]];if (t[num[y]]==sz) t[num[y]]=0; for (int i=l[num[y]];i<=y;++i) move(i,t[num[y]]); t[num[y]]=0; calc(num[y]); y=num[y]-1; } for (int i=x;i<=y;++i) { ++t[i]; if (t[i]==sz) t[i]=0; }}LL query(int x,int y){ LL ans=0; if (num[x]==num[y]) { for (int i=x;i<=y;++i) { ans+=cub[i][t[num[x]]]; if (ans>=Mod) ans-=Mod; } return ans; } if (x==l[num[x]]) x=num[x]; else { for (int i=x;i<=r[num[x]];++i) { ans+=cub[i][t[num[x]]]; if (ans>=Mod) ans-=Mod; } x=num[x]+1; } if (y==r[num[y]]) y=num[y]; else { for (int i=l[num[y]];i<=y;++i) { ans+=cub[i][t[num[y]]]; if (ans>=Mod) ans-=Mod; } y=num[y]-1; } for (int i=x;i<=y;++i) { ans+=Cub[i][t[i]]; if (ans>=Mod) ans-=Mod; } return ans;}int main(){ scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%I64d",&a[i]); block=n/floor(sqrt(n*sz)); if (!block) ++block; tot=(n-1)/block+1; for (int i=1;i<=n;++i) { num[i]=(i-1)/block+1; if (num[i]!=num[i-1]) r[num[i-1]]=i-1,l[num[i]]=i; } r[tot]=n; init(); scanf("%d",&m); for (int i=1;i<=m;++i) { opt=getchar(); while (opt!='C'&&opt!='Q') opt=getchar(); scanf("%d%d",&x,&y); if (x>y) swap(x,y); if (opt=='C') change(x,y); else printf("%I64d/n",query(x,y)); }}

总结

卡常数技巧 ①分块大小nn∗sz√ ②加法的取模改成加减 ③尽量不用乘法用加减


上一篇:SRM150_DIV2

下一篇:0005 控制语句

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表