「PMOI-3」期望乘积 题解
Description
ducati 热爱定义一些奇奇妙妙的东西。
-
定义两个序列不同,当且仅当它们的长度不同,或者它们长度相同但存在至少一组对应位上的值不同。
-
定义序列
A 的权值为A 中所有数的乘积。 -
定义序列间的可达如下:
- 做恰好
t 次操作,每次操作选择A 的一个子区间(注意,选定的子区间可以为空)并将子区间中的数加1 ;若存在一种操作方案,使得操作结束后A 与B 完全相同 ,则称A 可达B 。
- 做恰好
-
定义序列
A 的优美值为所有A 可达的不同序列的权值和。
现在,ducati 拥有了一个长度为
你能帮帮好奇的他吗?你只需要输出每个答案对
Solution
Subtask 1
爆搜即可。
期望得分
Subtask 2
不难发现,我们实际上就是要求出,所有满足对
考虑
令
根据 P1969 的套路,不难得到状态转移式:
第一个
边界:
答案:
时间复杂度
Subtask 3
为了方便叙述,我们将这个三维
考虑使用矩阵乘法来完成这个转移。
我们先处理出序列中每个位置的转移矩阵并建立出一棵线段树,维护区间内的矩阵乘积。
每次询问的时候,我们在线段树上找到那
时间复杂度
期望得分
Subtask 4
考虑优化。
在每次询问的时候,我们先定义一个向量,其
考虑到当
期望得分
Summary
本题主要考察了选手的
值得一提的是,本题似乎还可以通过矩阵前缀和以及矩阵求逆来解出。它虽然时间复杂度更为优秀(少一个
Code
#include <bits/stdc++.h>
#define PA pair<int,int>
#define fi first
#define se second
#define MP make_pair
using namespace std;
const int maxl=100005,mod=10007;
int read(){
int s=0,w=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') w=-w;ch=getchar();}
while (ch>='0'&&ch<='9'){s=s*10+(ch^'0');ch=getchar();}
return s*w;
}
int n,q,t,blo,now,ans,le,ri,tot;
int a[maxl],pos[11][11],lis[11];
struct Matrix{int a[11][11];}tmp[maxl];
struct Segment_tree{
int lson,rson,prod;
Matrix p;
}tree[maxl<<1];
Matrix operator * (const Matrix &x,const Matrix &y){
Matrix z;
for (int i=0;i<=blo;i++){
for (int j=0;j<=blo;j++){
z.a[i][j]=0;
for (int k=0;k<=blo;k++) z.a[i][j]+=x.a[i][k]*y.a[k][j];
z.a[i][j]%=mod;
}
}
return z;
}
void pushup(int rt){
int l=tree[rt].lson,r=tree[rt].rson;
tree[rt].p = tree[l].p * tree[r].p;
tree[rt].prod = (tree[l].prod * tree[r].prod) % mod;
}
int build_tree(int l,int r,int rt){
rt=++tot;
if (l==r){
tree[rt].prod=a[l];
tree[rt].p=tmp[l];
return rt;
}
int mid=(l+r)>>1;
tree[rt].lson=build_tree(l,mid,0);
tree[rt].rson=build_tree(mid+1,r,0);
pushup(rt);
return rt;
}
int query_prod(int nl,int nr,int l,int r,int rt){
if (nl<=l&&r<=nr){
return tree[rt].prod;
}
int mid=(l+r)>>1,res=1;
if (nl<=mid) res = query_prod(nl,nr,l,mid,tree[rt].lson);
if (nr>mid) res *= query_prod(nl,nr,mid+1,r,tree[rt].rson);
return res%mod;
}
void query_seg(int nl,int nr,int l,int r,int rt){
if (nl<=l && r<=nr){
int tmp[10];
for (int i=0;i<=blo;i++) tmp[i]=0;
for (int i=0;i<=blo;i++){
for (int j=0;j<=blo;j++){
tmp[j] += lis[i] * tree[rt].p.a[i][j];
}
}
for (int i=0;i<=blo;i++) lis[i] = tmp[i]%mod;
return;
}
int mid=(l+r)>>1;
if (nl<=mid) query_seg(nl,nr,l,mid,tree[rt].lson);
if (nr>mid) query_seg(nl,nr,mid+1,r,tree[rt].rson);
}
signed main(){
n=read(),q=read(),t=read();
for (int i=1;i<=n;i++) a[i]=read();
if (t){
blo=((t+1)*(t+2))/2;
for (int i=0;i<=t;i++){
for (int j=i;j<=t;j++){
pos[i][j]=now;
now++;
}
}
for (int i=1;i<=n;i++){
for (int j=0;j<=t;j++){
int val=a[i]+j;
for (int k=j;k<=t;k++){
for (int p=0;p<=k;p++){
if (p<j){
if (k-j+p>=0) tmp[i].a[pos[p][k-j+p]][pos[j][k]]=val;
}
else tmp[i].a[pos[p][k]][pos[j][k]]=val;
}
}
}
}
}
build_tree(1,n,1);
while (q--){
le=read(),ri=read();
if (t==0){
ans = query_prod(le,ri,1,n,1);
}
else{
memset(lis,0,sizeof(lis));
lis[0]=1,ans=0;
query_seg(le,ri,1,n,1);
for (int i=0;i<=t;i++) ans=(ans+lis[pos[i][t]])%mod;
}
printf("%d\n",ans);
}
return 0;
}