【Codeforces 633H】Fibonacci-ish II
首先要知道斐波那契数列一个很有趣的性质:\[F_n = F_{a+1} * F_b + F_a * F_{b-1}\ (a+b=n)\]对所有的整数n,a,b都是满足的,包括负数。式子用矩阵就可以推了。
首先为将a的值离散化,建棵权值线段树,维护子树中的当前在a的potential序列中的数对答案的贡献。在权值线段树里进行加点删点操作,就可以快速地求出当前的数组a的potential序列。
用莫队算法处理询问。接下来我们要在\(O(log(n))\)的时间内处理单步操作。
每次新加入一个点x,那么所有权值大于x的数对应的斐波那契数列中的项都要右移一位。如果每次暴力修改的话复杂度是\(O(n)\)的,所以要用上lazy_tag。
一眼看过去这个lazy_tag好像不可加的样子。要注意,斐波那契数列在存储时,存相邻的两位往往有奇效。
\[
\ F(k+1)*(a_{1}*F(i)+a_{2}*F(i+1) \cdots) \\
\ +F(k)*(a_{1}*F(i-1)+a_{2}*F(i)+\cdots) \\
\ =(a_{1}*F(i+k)+a_{2}*F(i+k+1))
\]
(这个排版太差了...宽度太小了QAQ)
显然,只要维护两个序列就能在\(O(1)\)的时间内打标记和传标记了。
时间复杂度是\(O(n \sqrt{n} *log(n)\)的,每次在线段树上的操作尽量一次完成,否则可能因为常数太大而T掉。(明明是你自己代码自带大常数
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=30010;
int p;
int fi[maxn];
inline int f(int x) {if (x>=0) return fi[x];else return ((-x)&1)?fi[-x]:p-fi[-x];}
struct Segment_Tree
{
int sum1[maxn<<2],sum2[maxn<<2];
int size[maxn<<2];
int tag_k[maxn<<2];
int n;
#define ls (o<<1)
#define rs (o<<1|1)
#define mid ((l+r)>>1)
void Update(int o) {sum1[o]=(sum1[ls]+sum1[rs])%p;sum2[o]=(sum2[ls]+sum2[rs])%p;size[o]=size[ls]+size[rs];}
void Mark(int o,int k)
{
if (k==1) {int x1=sum1[o];sum1[o]+=sum2[o],sum2[o]=x1;if (sum1[o]>p) sum1[o]-=p;tag_k[o]++;return;}
int x1=(sum1[o]*f(k+1)+sum2[o]*f(k))%p,x2=(sum1[o]*f(k)+sum2[o]*f(k-1))%p;
sum1[o]=x1;sum2[o]=x2;tag_k[o]+=k;
}
void Push_Down(int o)
{
if (tag_k[o])
{
Mark(ls,tag_k[o]);
Mark(rs,tag_k[o]);
tag_k[o]=0;
}
}
void Change(int o,int l,int r,int x,int v,int w,int c)
{
if (l==r)
{
if (c==-1) size[o]=sum1[o]=sum2[o]=0;
else size[o]=1,sum1[o]=v*f(w+1)%p,sum2[o]=v*f(w)%p;
return;
}
Push_Down(o);
if (x<=mid) Change(ls,l,mid,x,v,w,c),Mark(rs,c);
else Change(rs,mid+1,r,x,v,w+size[ls],c);
Update(o);
}
void Change(int x,int v,int c) {Change(1,1,n,x,v,0,c);}
int Result() {return sum1[1];}
}T;
int a[maxn],temp[maxn];
int cnt[maxn];
int ans[maxn];
int n,q,blocksize,ts;
struct Query {int l,r,block,id;bool operator< (const Query &b) const {return block<b.block || block==b.block && r<b.r;}}Q[maxn];
int main()
{
scanf("%d%d",&n,&p);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
memcpy(temp,a,sizeof(temp));
sort(temp+1,temp+1+n);
ts=unique(temp+1,temp+1+n)-(temp+1);
for (int i=1;i<=n;i++) a[i]=lower_bound(temp+1,temp+1+ts,a[i])-temp;
for (int i=1;i<=ts;i++) temp[i]%=p;
T.n=ts;
fi[1]=1;fi[2]=1;
for (int i=3;i<=ts+2;i++) fi[i]=(fi[i-1]+fi[i-2])%p;
blocksize=sqrt(n);
scanf("%d",&q);
for (int i=1;i<=q;i++) scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].block=Q[i].l/blocksize,Q[i].id=i;
sort(Q+1,Q+1+q);
for (int i=1,l=1,r=0;i<=q;i++)
{
while (r<Q[i].r) {r++;cnt[a[r]]++;if (cnt[a[r]]==1) T.Change(a[r],temp[a[r]], 1);}
while (r>Q[i].r) {cnt[a[r]]--;if (cnt[a[r]]==0) T.Change(a[r],temp[a[r]],-1);r--;}
while (l>Q[i].l) {l--;cnt[a[l]]++;if (cnt[a[l]]==1) T.Change(a[l],temp[a[l]], 1);}
while (l<Q[i].l) {cnt[a[l]]--;if (cnt[a[l]]==0) T.Change(a[l],temp[a[l]],-1);l++;}
ans[Q[i].id]=T.Result();
}
for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}
评论 (0)