P6533 RELATIVNOST 题解

link\color{deeppink}link

考试时写了个线段树维护矩阵,爆完了。。

你要求至少有 cc 个人买彩照的方案数,换一下,我们求有 i[0,c)i\in[0,c) 个人买彩照的方案数,然后用总方案数 Π(ai+bi)\Pi(a_i+b_i) 减一下即可。

fi,jf_{i,j} 表示前 ii 个人有 jj 个人买彩照的方案数。

显而易见的,方案数与 ii 的顺序无关,因为调换某些人,答案不会变。再加上有修改操作,可以想到用线段树优化。

那么 fx,if_{x,i} 则表示 xx 管辖的区间内有 ii 个人买彩照的方案数,显然它的状态可以由左右儿子用卷积的方式转移过来,有:

fx,i=j=0ifls,j×frs,ij f_{x,i}=\sum_{j=0}^i f_{ls,j}\times f_{rs,i-j}

的形式,其中边界的叶子节点有:

fx,0=bx,fx,1=axf_{x,0}=b_x,f_{x,1}=a_x

注意随时更新总方案数。

codecode

#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';
	}
}