Gracemeria 侵攻作战

题目描述

给定一个长度为 n的序列 a,初始都是 0,和一个正整数 k。

现有 \(q\) 次操作,每次操作给定 \(i,v\),表示给序列 a的后缀 \(a_{[i,n]}\) 加上 v。

每次操作后,请你输出 所有数在序列中出现次数的 k次方和 对 20051131取模的结果。

20051131 是质数。

输入格式

第一行三个整数 n,q,k。

接下来 q 行,每行两个整数,表示这次操作的 i,v。

输出格式

q行,每行一个整数,表示这次操作之后所有数在序列中出现次数的 k 次方和对 20051131 取模的值。

输入输出样例

输入 #1

5 5 2
1 1
2 1
3 1
4 1
5 1

输出 #1

25
17
11
7
5

说明/提示

第一次操作后,有 5个 1,答案为 \(5^2=25\)

第二次操作后,有 1 个 1 和 4 个 2,答案为 \(1^2+4^2=17\)

类似的,答案分别为 \(1^2+1^2+3^2=11,1^2+1^2+1^2+2^2=7,5\times 1^2=5\)


\(Subtask 1(20 pts):n,q\leq 2\times 10^3\)

\(Subtask 2(40 pts):n\leq 2\times 10^3\)

\(Subtask 3(40 pts):无特殊限制\)

\(对 100\% 的数据,保证 1\leq n,v,k\leq 20051130,1\leq q\leq 5\times 10^5,1\leq i\leq n\)

题目解析

水题

因为修改区间为\([i,n]\),所以显然序列\(a\)\([1,n]\)项可以划分为单调递增的连续块

形如\(1,1,2,2,3,3,4,4,4\)

所以维护连续相同区间的左边界,每次修改时,查找左边界小于等于\(i\)的最大值,以\(i\)为断点划分为2段,重新计算,并插入新的左边界\(i\),新的区间即为\([x,i)\)

注意小坑:当\(i\)与左边界\(x\)相同时,要删除原来的\(x\)在插入新的左边界\(i\),因为他们代表的是同一个区间的左边界(不存在区间\([x,x)\)

这里使用STL的set方便进行排序查找删除操作

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include<set>
using namespace std;
typedef long long ll;
set<int> s;
int Mod=20051131;
ll ans;
ll qpow(ll x,int k){
    ll res=1;
    while (k){
        if (k&1) res=res*x%Mod;
        x=x*x%Mod;
        k>>=1;
    }
    return res;
}
int main(){
    int n,q,k,v,x;
    cin>>n>>q>>k;
    ans=qpow(n,k);
    s.insert(1);
    s.insert(n+1);
    for (int i=1;i<=q;i++){
        scanf("%d %d",&x,&v);
        set<int>::iterator it;
        it=s.lower_bound(x);
        int l=*it,r=*(++it);
        if (l>x) {
            r=*(--it);l=*(--it);
        }
        //cout<<l<<endl;
        ans=(ans-qpow(r-l,k)+Mod)%Mod;
        ans=((ans+qpow(x-l,k))%Mod+qpow(r-x,k))%Mod;
        if (x==l) s.erase(l);
        s.insert(x);
        printf("%lld\n",ans);
    }
}
posted @ 2021-08-11 22:06  Z-Y-Y-S  阅读(32)  评论(0编辑  收藏  举报