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

题目描述

[SD2014集训]查询(分块+数学相关)_第1张图片
[SD2014集训]查询(分块+数学相关)_第2张图片
这里写图片描述
[SD2014集训]查询(分块+数学相关)_第3张图片

题解

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

代码

#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define Mod 329701061
#define sz 48
#define N 100005

char 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;j1]*cub[i][j-1]%Mod*cub[i][j-1]%Mod;
    }
    for (int i=1;i<=tot;++i)
        for (int j=0;jfor (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;ifor (int i=rad;ifor (int i=0;ivoid calc(int id)
{
    for (int i=0;i0;
        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]];ifor (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]];iif (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));
    }
}

总结

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

你可能感兴趣的:(题解,数学相关,分块)