题解:P7576 「PMOI-3」期望乘积
题目传送门
题目大意
给定一个序列 \(a\),每次询问一段区间 \(l\) 至 \(r\),可以进行 \(0\) 至 \(t\) 次操作,每次选定一个区间将其所有数 \(+1\)。给出最终序列所有数乘积之和,对 \(10007\) 取模。
思路分析
可以先考虑暴力 \(O(nq)\) 如何做。假设我们现在已经知道操作序列 \(c\),即每个数加了多少次,那么可以求出总操作次数。即 \(\sum_{i=1}^{n-1} \max(0,c_{i+1}-c_i)\),这里详见 P1969 [NOIP 2013 提高组] 积木大赛。
由此,可以设计出一个状态 \(dp_{i,j,k}\) 为前 \(i\) 个数,总操作次数为 \(j\),\(c_i\) 为 \(k\)。转移是显然的:
初始值:\(dp_{0,0,0}=1\)。
时间复杂度 \(O(nq \times (t+1)^2)\)。
可以尝试初步优化,发现 \(k \le j\),则有用的信息总数为 \(\frac{(t+1) \times (t+2)}{2}\),可以估算成 \(t^2\)。
接下来考虑如何优化掉 \(n\)。发现如果知道了 \(dp_{i-1}\) 的所有信息,那么 \(dp_i\) 是可以通过固定的几个 \(dp_{i-1}\) 值相加得到的。所以考虑矩阵乘法,答案即为 \(l\) 至 \(r\) 的所有矩阵乘在一起,然后乘上初始矩阵,区间乘可以用线段树维护。为了方便维护,将 \(dp_{i,j,k}\) 映射到 \(dp_{i,j \times (t+1)+k}\)。
显然可以预处理出来不同的 \(t\) 值所对应的矩阵,然后乘上 \(a_i+k\) 即可,加上建树的时间复杂度为 \(O(n \times t^6)\)。
然而查询的时间复杂度是 \(q \log n \times t^6\),很明显会超时。但其实不需要线段树两个矩阵相乘,只需要一个初始行矩阵就可以,这样乘法的复杂度就降到了 \(t^4\)。
所以总时间复杂度大概是 \(O(n \times t^6 + q \log n \times t^4)\),这个时间复杂度可以通过该题。
ACcode
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INT_MAX (int)(1e18)
#define mid (l+r>>1)
const int N=1e5+10;
const int mod=10007;
int n,q,k;
int a[N],dp[N][16];
int len[4]={0,2,5,9};
int Ju[3][10][10]=
{
{
{1,0,1},
{0,1,0},
{0,1,1}
},
{
{1,0,1,0,0,1},
{0,1,0,0,1,0},
{0,1,1,0,0,1},
{0,0,0,1,0,0},
{0,0,0,1,1,0},
{0,0,0,1,1,1}
},
{
{1,0,1,0,0,1,0,0,0,1},
{0,1,0,0,1,0,0,0,1,0},
{0,1,1,0,0,1,0,0,0,1},
{0,0,0,1,0,0,0,1,0,0},
{0,0,0,1,1,0,0,0,1,0},
{0,0,0,1,1,1,0,0,0,1},
{0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,1,1,0,0},
{0,0,0,0,0,0,1,1,1,0},
{0,0,0,0,0,0,1,1,1,1}
}
};
struct node{
int ju[10][10];
void New_Node(int val){
int las=0,nxt=1,add=2;
for(int j=0;j<=len[k];j++){
for(int i=0;i<=len[k];i++){
if(j==nxt) las=nxt,nxt+=add,add++;
ju[i][j]=Ju[k-1][i][j]*(val+j-las);
}
}
}
}tr[N<<2],chu,sum;
inline int read(){
int t=0,f=1;
register char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?(-1):(f),c=getchar();
while(c>='0'&&c<='9') t=(t<<3)+(t<<1)+(c^48),c=getchar();
return t*f;
}
//将三维 dp i,j,k 映射到 i,j*(t+1)+k 上
/*
30pts
int solve1(int L,int R){
for(int i=0;i<=3;i++)
for(int j=0;j<=3;j++)
dp[L-1][i*(k+1)+j]=0;
dp[L-1][0]=1;
for(int i=L;i<=R;i++)
for(int j=0;j<=k;j++)
for(int l=0;l<=j;l++){
dp[i][j*(k+1)+l]=0;
cout<<"hou:"<<j*(k+1)+l<<"\n";
for(int ls=0;ls<=j;ls++)
if(ls<l) dp[i][j*(k+1)+l]+=dp[i-1][(j-l+ls)*(k+1)+ls],cout<<(j-l+ls)*(k+1)+ls<<" ";
else dp[i][j*(k+1)+l]+=dp[i-1][j*(k+1)+ls],cout<<j*(k+1)+ls<<" ";
dp[i][j*(k+1)+l]=dp[i][j*(k+1)+l]%mod*(a[i]+l)%mod;
cout<<"\n";
}
int res=0;
for(int i=0;i<=k;i++)
for(int j=0;j<=i;j++) res=(res+dp[R][i*(k+1)+j])%mod;
return res;
}
*/
node operator *(const node &x,const node &y){
node z;
for(int i=0;i<=len[k];i++)
for(int j=0;j<=len[k];j++){
z.ju[i][j]=0;
for(int l=0;l<=len[k];l++) z.ju[i][j]+=x.ju[i][l]*y.ju[l][j];
z.ju[i][j]%=mod;
}
return z;
}
void pushup(int bian){
tr[bian]=tr[bian<<1]*tr[bian<<1|1];
}
void build(int bian,int l,int r){
if(l==r){tr[bian].New_Node(a[l]);return;}
build(bian<<1,l,mid);
build(bian<<1|1,mid+1,r);
pushup(bian);
}
void query(int bian,int l,int r,int L,int R){
if(L<=l&&R>=r){
for(int j=0;j<=len[k];j++){
sum.ju[0][j]=0;
for(int l=0;l<=len[k];l++)
sum.ju[0][j]+=chu.ju[0][l]*tr[bian].ju[l][j];
sum.ju[0][j]%=mod;
}
for(int i=0;i<=len[k];i++) chu.ju[0][i]=sum.ju[0][i];
return;
}
if(L<=mid) query(bian<<1,l,mid,L,R);
if(R>mid) query(bian<<1|1,mid+1,r,L,R);
}
int solve(int L,int R){
chu.ju[0][0]=1;
for(int i=1;i<=len[k];i++) chu.ju[0][i]=0;
query(1,1,n,L,R);
int res=0;
for(int i=0;i<=len[k];i++) res=res+chu.ju[0][i];
return res%mod;
}
void debug(int bian){
for(int i=0;i<=len[k];i++){
for(int j=0;j<=len[k];j++)
cout<<tr[bian].ju[i][j]<<" ";
cout<<"\n";
}
cout<<"\n";
}
signed main(){
n=read(),q=read(),k=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(int i=1;i<=q;i++){
int l=read(),r=read();
cout<<solve(l,r)<<"\n";
}
return 0;
}

浙公网安备 33010602011771号