考试时写了个线段树维护矩阵,爆完了。。
你要求至少有 个人买彩照的方案数,换一下,我们求有 个人买彩照的方案数,然后用总方案数 减一下即可。
令 表示前 个人有 个人买彩照的方案数。
显而易见的,方案数与 的顺序无关,因为调换某些人,答案不会变。再加上有修改操作,可以想到用线段树优化。
那么 则表示 管辖的区间内有 个人买彩照的方案数,显然它的状态可以由左右儿子用卷积的方式转移过来,有:
的形式,其中边界的叶子节点有:
注意随时更新总方案数。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+50,mod=10007;
int a[N],b[N],q,n,c,tot,f[N<<2][23];
#define ls (x<<1)
#define rs (x<<1|1)
#define mid ((l+r)>>1)
void make_f(int x,int p)
{
a[p]%=mod,b[p]%=mod;
f[x][0]=b[p];
f[x][1]=a[p];
}
void mul(int x)
{
memset(f[x],0,sizeof f[x]);
for(int i=0;i<=c;i++)
for(int j=0;j<=i;j++)
f[x][i]=(f[x][i]+f[ls][j]*f[rs][i-j])%mod;
}
void build(int x,int l,int r)
{
if(l==r)
{
make_f(x,l);
return;
}
build(ls,l,mid);build(rs,mid+1,r);
mul(x);
}
void change(int x,int l,int r,int t)
{
if(l==r)
{
make_f(x,l);
return;
}
if(t<=mid)change(ls,l,mid,t);
else change(rs,mid+1,r,t);
mul(x);
}
int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
cin>>n>>c;
for(int i=1;i<=n;i++)cin>>a[i];tot=1;
for(int i=1;i<=n;i++)cin>>b[i],tot=tot*(a[i]+b[i])%mod;
build(1,1,n);
cin>>q;
while(q--)
{
int x,y,z;
cin>>x>>y>>z;
tot=tot*ksm(a[x]+b[x],mod-2)%mod*(y+z)%mod;
a[x]=y,b[x]=z;
change(1,1,n,x);
int ans=tot;
for(int i=0;i<c;i++)ans=(ans-f[1][i]+mod)%mod;
cout<<ans<<'\n';
}
}