P4828 Nagisa loves Tomoya 解题报告

P4828 Nagisa loves Tomoya 解题报告

67

1. 原题 P4828 Nagisa loves Tomoya

题目描述

可爱的古河渚得到了一个序列。她定义一次操作为 将所有的 \(a_i\) 变为 \(a_i+a_{(i\ mod \ n+1)}\)
然后她会向你进行 \(Q\) 次询问,每次问你 \(x,y\) ,意义为在第 \(x\) 次操作后位置 $y $ 的元素是多少。

这个问题太简单了,我们的 Nagisa 轻易地就解决了这个问题。请问您能解决这个问题吗?

输入格式

一个整数 \(n\),表示序列的长度。

接下来一行 \(n\) 个整数,表示这个序列。

接下来一行一个整数 \(Q\),表示询问次数

接下来 \(Q\) 行,每行两个整数 \(x,y\),意义如题所示。

输出格式

共 \(Q\) 行,每行一个整数表示询问的答案,请注意对 \(998244353\) 取模。

输入输出样例 #1
输入 #1
5
1 2 3 4 5
2
1 2
2 2
输出 #1
5
12
说明/提示

对于\(30 \%\)的数据,满足:
\(1 \leq n \leq 100\)

\(1 \leq x \leq 100\)

\(1 \leq Q \leq 100\)

对于 \(50\%\) 的数据:

\(1\leq n \leq 100000\)

\(1 \leq x \leq 500\)

\(1 \leq Q \leq 100\)

对于 \(100 \%\) 的数据,满足

\(1 \leq n \leq 10^6\)

\(1 \leq a_i \leq 10^9\)

\(1 \leq x \leq 2000\)

\(1 \leq Q \leq 10^4\)

2. 题目概述

给个序列,下标从 \(1\) 开始,有个操作,使得 \(a_i = a_{(i \bmod n) + 1}(1 \le i \le n)\),\(q\) 个询问,对于每个询问,问 \(x\) 次操作后,第 \(y\) 个位置的数是多少

3. 题解

题目分析

我们可以想到打一个暴力,按询问的 \(x\) 排序,依此操作计算,这样最坏复杂度是 \(O(nQ)\) 的很明显会 T 于是我们拿到了 50pts :

暴力代码

50pts

#include<iostream>
#include<algorithm>
#define int long long
using namespace std;

const int N = 1e6 + 10;
const int mod = 998244353;
typedef long long LL;

int n;
int a[N];

struct Query{
    int x, y, id;
}q[N];
bool cmp(Query a, Query b){
    return a.x < b.x;
}

void modify(){
    int tmp = a[1];
    for(int i = 1 ; i < n ; i ++) a[i] = (a[i] + a[i + 1]) % mod;
    a[n] = (a[n] + tmp) % mod;
}

int ans[N];

signed main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin>>n;
    for(int i = 1 ; i <= n ; i ++) cin>>a[i];

    int t;
    cin>>t;
    for(int i = 1 ; i <= t ; i ++){cin>>q[i].x>>q[i].y;q[i].id = i;}
    sort(q + 1, q + t + 1, cmp);

    int op = 0;
    for(int i = 1 ; i <= t ; i ++){
        for(; op < q[i].x ; op ++) modify();
        ans[q[i].id] = a[q[i].y]; 
    }

    for(int i = 1 ; i <= t ; i ++) cout<<ans[i]<<'\n';

    return 0;
}

继续分析

这咋办啊,打假了...那就从头开始想,我们发现这些操作其实就是把每个数都 += 下一个数(到 \(n\) 时加第 \(1\) 个数,不断递推下去,那我们来打个表找找规律:

5
1 2 3 4 5
1
5 2

不进行操作时:\(a_2\)

一次操作时:\(a_2+a_3\)

两次操作时:\(a_2+a_3+a_3+a_4\)

三次操作时:\(a_2+a_3+a_3+a_3+a_4+a_4+a_4+a_5\)

...

初见端倪,我们写下来

\[a_2 \\ a_2 \quad a_3 \\ a_2 \quad 2a_3 \quad a_4 \\ a_2 \quad 3a_3 \quad 3a_4 \quad a_5 \\ a_2 \quad 4a_3 \quad 6a_4 \quad 4a_5 \quad a_1 \\ \]

我们把系数写下来

\[1 \\ 1 \quad 1 \\ 1 \quad 2 \quad 1 \\ 1 \quad 3 \quad 3 \quad 1 \\ 1 \quad 4 \quad 6 \quad 4 \quad 1 \\ \]

很好这是一个杨辉三角

那我们做出来了

算法过程

  1. 预处理 \(2000\) 层杨辉三角
  2. 每次遇到询问 \(x \space y\) 时,直接在第 \(x\) 层杨辉三角且在数组 \(a\) 的第 \(y\) 个位置向后推,推到 \(n\) 就回到 \(1\) 直到把这一层杨辉三角推完算出答案

代码示例

别忘了取模

#include<iostream>
#include<algorithm>
#define int long long
using namespace std;

const int N = 1e6 + 10;
const int M = 2010;
const int mod = 998244353;
typedef long long LL;

int t[M][M];

void init(int n){
    t[0][1] = 1;
    for(int i = 1 ; i <= n ; i ++){
        for(int j = 1 ; j <= i + 1 ; j ++){
            t[i][j] = (t[i - 1][j] + t[i - 1][j - 1]) % mod;
            if(t[i][j] == 0) t[i][j] = 1;
        }
    }
}

int a[N];

signed main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    init(2000);
    
    int n;
    cin>>n;
    for(int i = 1 ; i <= n ; i ++){
        cin>>a[i];
    }

    int q;
    cin>>q;
    for(int i = 1 ; i <= q ; i ++){
        int x, y;
        cin>>x>>y;

        int ans = 0;
        for(int j = 1, pos = y ; j <= x + 1 ; j ++){
            ans = (ans + a[pos] * t[x][j] % mod) % mod;
            pos = pos % n + 1;
        }
        cout<<ans<<'\n';
    }

    return 0;
}
posted @ 2026-10-01 08:31  神烦doge  阅读(5)  评论(0)    收藏  举报