Codeforces 575A

这是个线性递推,可以用 2 ∗ 2 2*2 22的矩乘转移,没有特殊位置的话可以直接快速幂。
现在有特殊数字,这也不难。考虑按特殊位置分段,段内直接转移,特殊位置再特殊乘一下。有个比较麻烦的情况是两个特殊位置可能距离 < n <n <n,这种情况下我们可以拿个线段树查询一下区间矩阵乘积。
时间复杂度 O ( ( n + m ) ( log ⁡ n + log ⁡ V ) ) \mathcal O((n+m)(\log n+\log V)) O((n+m)(logn+logV))

#include <bits/stdc++.h>
#define last last2
 
using namespace std;
 
typedef long long ll;
 
int MOD;
 
struct Matrix {
  int num[2][2],n,m;
  Matrix() {}
  Matrix(int a,int b):n(a),m(b) {memset(num,0,sizeof(num));}
};
 
Matrix operator * (Matrix a,Matrix b) {
  Matrix c(a.n,b.m);
  for(int i=0;i<c.n;i++)
    for(int j=0;j<c.m;j++)
      for(int k=0;k<a.m;k++) c.num[i][j]=(c.num[i][j]+(ll)a.num[i][k]*b.num[k][j])%MOD;
  return c;
}
 
Matrix operator ^ (Matrix a,ll k) {
  Matrix ans(a.n,a.m);
  for(int i=0;i<ans.n;i++) ans.num[i][i]=1;
  while (k) {
  	if (k&1) ans=ans*a;
  	a=a*a;
  	k>>=1;
  }
  return ans;
}
 
Matrix fir[50005];
 
namespace SGT {
 
Matrix mulv[200000];
 
void build(int l,int r,int o) {
  if (l==r) mulv[o]=fir[l];
  else {
  	int m=((l+r)>>1);
  	build(l,m,o*2);
  	build(m+1,r,o*2+1);
  	mulv[o]=mulv[o*2+1]*mulv[o*2];
  }
}
 
Matrix query(int l,int r,int o,int lx,int rx) {
  if (l>=lx&&r<=rx) return mulv[o];
  else {
  	int m=((l+r)>>1);
  	if (m>=rx) return query(l,m,o*2,lx,rx);
  	if (m<lx) return query(m+1,r,o*2+1,lx,rx);
  	return query(m+1,r,o*2+1,lx,rx)*query(l,m,o*2,lx,rx);
  }
}
 
}
 
map <ll,int> mp;
ll val[200005];
int num[200005];
 
Matrix rmul[50005],lmul[50005];
 
int main() {
  ll k;
  scanf("%lld%d",&k,&MOD);
  if (!k) {
  	puts("0");
  	return 0;
  }
  int n;
  scanf("%d",&n);
  for(int i=0;i<n;i++) scanf("%d",&num[i]);
  for(int i=1;i<=n;i++) {
  	fir[i]=Matrix(2,2);
  	fir[i].num[0][0]=num[(i-1)%n]%MOD;
	fir[i].num[0][1]=num[((i-2)%n+n)%n]%MOD;
  	fir[i].num[1][0]=1%MOD;
  }
  lmul[0]=Matrix(2,2);
  lmul[0].num[0][0]=lmul[0].num[1][1]=1;
  for(int i=1;i<=n;i++) lmul[i]=fir[i]*lmul[i-1];
  rmul[n+1]=Matrix(2,2);
  rmul[n+1].num[0][0]=rmul[n+1].num[1][1]=1;
  for(int i=n;i>0;i--) rmul[i]=rmul[i+1]*fir[i];
  SGT::build(1,n,1);
  Matrix all=SGT::mulv[1];
  int m;
  scanf("%d",&m);
  int cnt=0;
  for(int i=1;i<=m;i++) {
  	ll x;
  	int y;
  	scanf("%lld%d",&x,&y);
  	mp[x]=y%MOD;
  	val[++cnt]=x+1;val[++cnt]=x+2;
  }
  val[++cnt]=k;
  sort(val+1,val+cnt+1);
  cnt=unique(val+1,val+cnt+1)-val-1;
  Matrix ans(2,1);
  ans.num[0][0]=1%MOD;
  ll last=1;
  for(int i=1;i<=cnt;i++) {
  	ll x=val[i];
  	if (last+1<x) {
  		ll l=last+1,r=x-1;
  		if ((l-1)/n!=(r-1)/n) {
  			ans=rmul[(l-1)%n+1]*ans;
  			if ((l-1)/n+1<(r-1)/n) ans=(all^((r-1)/n-(l-1)/n-1))*ans;
  			ans=lmul[(r-1)%n+1]*ans;
		  }
		else ans=SGT::query(1,n,1,(l-1)%n+1,(r-1)%n+1)*ans;
	  }
	Matrix cur(2,2);
	cur.num[0][0]=(mp.count(x-1)?mp[x-1]:num[(x-1)%n]%MOD);
	cur.num[0][1]=(mp.count(x-2)?mp[x-2]:num[((x-2)%n+n)%n]%MOD);
	cur.num[1][0]=1%MOD;
	ans=cur*ans;
	if (x==k) break;
	last=x;
  }
  printf("%d\n",ans.num[0][0]);
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值