AT2371-[AGC013E]Placing Squares【矩阵乘法】
正题
题目链接:https://www.luogu.com.cn/problem/AT2371
题目大意
给出\(n\)和\(m\)个数\(b\)。
求所有满足以下要求的序列\(a\)
- 和为\(n\)
- 对于所有\(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;
}