正题
题目链接:https://www.luogu.com.cn/problem/AT2371
题目大意
给出nnn和mmm个数bbb。
求所有满足以下要求的序列aaa
- 和为nnn
- 对于所有bib_ibi不存在任何一个前缀和为bib_ibi。
一个序列的贡献为所有数的二次方和,求所有合法序列的贡献。
1≤n≤109,1≤m≤1051\leq n\leq 10^9,1\leq m\leq 10^51≤n≤109,1≤m≤105
解题思路
它要是nnn开到101810^{18}1018我就会了(雾)
显然地我们是到每个mmm处然后容斥,所以如果这么想你就错了
这个平方很难统计,考虑转换成另一个模型,在分出的每一段中选出两个位置(有序,可重)放上一个红球和一个蓝球,求方案数。
那么我们就有一个dpdpdp的想法,设fi,jf_{i,j}fi,j表示表示目前到第iii个位置,目前最后的一段已经放了jjj个球了,当两个球位置不同时的转移乘二即可。
这样在有限制的位置和没有限制的位置就分别有了两个转移矩阵,分成mmm段乘起来就好了。
时间复杂度:O(mlogn)O(m\log n)O(mlogn)
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;
}