题目描述

给定一个长度为 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);
}
}