CF-1921-F-根号分治

1921-F 题目大意

有一个长为n的序列a,有q次询问,对于每次询问:

  • 给定s,d,k,请输出i=1kias+(i1)d

Solution

根号分治。

  • 对于dn的情况,直接暴力计算即可。
  • 对于dn的情况,这时需要预处理两个数组:pre,sum,这里pre[d][i]表示公差为d,最后一个元素为a[i]的数列的前缀和;sum[d][i]表示公差为d的数列,a[i]其项数的前缀和,递推式子如下:

pre[d][i+d]=pre[d][i]+a[i]

sum[d][i+d]=sum[d][i]+a[i](i/d+1)

这时可以O(1)的计算出询问结果为sum[d][s+dk]sum[d][s](pre[d][s+dk]pre[d][s])(s/d)

根号算法结合了暴力计算以及数列求和的优点,真正实现了时空平衡,复杂度均为O(nn)

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

const int N=1e5+10;
ll pre[400][N],sum[400][N];

void solve(){
    int n,q;
    cin>>n>>q;
    int B=sqrt(n);
    vector<ll> a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int d=1;d<=B;d++){
        for(int i=1;i<=n;i++){
            pre[d][i+d]=pre[d][i]+a[i];
            sum[d][i+d]=sum[d][i]+a[i]*(i/d+1);
        }
    }
    while(q--){
        int s,d,k;
        cin>>s>>d>>k;
        if(d<B){
            cout<<sum[d][s+d*k]-sum[d][s]-(pre[d][s+d*k]-pre[d][s])*(s/d)<<" ";
        }else{
            ll res=0;
            for(ll i=1;i<=k;i++){
                res+=a[s+d*(i-1)]*i;
            }
            cout<<res<<" ";
        }
    }
    cout<<'\n';
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @   fengxue-K  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示