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);
}
}