AT2371-[AGC013E]Placing Squares【矩阵乘法】

正题

题目链接:https://www.luogu.com.cn/problem/AT2371


题目大意

给出\(n\)\(m\)个数\(b\)
求所有满足以下要求的序列\(a\)

  1. 和为\(n\)
  2. 对于所有\(b_i\)不存在任何一个前缀和为\(b_i\)

一个序列的贡献为所有数的二次方和,求所有合法序列的贡献。

\(1\leq n\leq 10^9,1\leq m\leq 10^5\)


解题思路

它要是\(n\)开到\(10^{18}\)我就会了(雾)

显然地我们是到每个\(m\)处然后容斥,所以如果这么想你就错了

这个平方很难统计,考虑转换成另一个模型,在分出的每一段中选出两个位置(有序,可重)放上一个红球和一个蓝球,求方案数。

那么我们就有一个\(dp\)的想法,设\(f_{i,j}\)表示表示目前到第\(i\)个位置,目前最后的一段已经放了\(j\)个球了,当两个球位置不同时的转移乘二即可。

这样在有限制的位置和没有限制的位置就分别有了两个转移矩阵,分成\(m\)段乘起来就好了。

时间复杂度:\(O(m\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll S=3,P=1e9+7;
struct Matrix{
	ll a[S][S];
}c,f,g,ans;
ll n,m;
Matrix operator*(const Matrix &a,const Matrix &b){
	memset(c.a,0,sizeof(c.a));
	for(ll i=0;i<S;i++)
		for(ll j=0;j<S;j++)
			for(ll k=0;k<S;k++)
				(c.a[i][j]+=a.a[i][k]*b.a[k][j]%P)%=P;
	return c;
}
void power(Matrix x,ll b){
	while(b){
		if(b&1)ans=ans*x;
		x=x*x;b>>=1;
	}
	return;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	f.a[0][0]=2;f.a[0][1]=1;f.a[0][2]=1;
	f.a[1][1]=1;f.a[1][2]=2;f.a[1][0]=2;
	f.a[2][2]=1;f.a[2][0]=1;
	g=f;g.a[0][0]=1;g.a[1][0]=0;g.a[2][0]=0;
	ll last=0;ans.a[0][0]=1;
	for(ll i=1,x;i<=m;i++){
		scanf("%lld",&x);
		power(f,x-last-1);
		ans=ans*g;
//		ans.a[0][0]*=-1;
		last=x;
	}
	power(f,n-last);
	printf("%lld\n",ans.a[0][2]);
	return 0;
}
posted @ 2021-11-28 18:06  QuantAsk  阅读(30)  评论(0编辑  收藏  举报