RELATIVNOST

link

建议管理大大评紫,理由:巧妙的线段树优化动态规划。

首先考虑补集转化。所有顾客不受限制的购买方案显然是 \(\prod\limits_ia_i+b_i\) ,即每个顾客买黑白画和彩色画的方案相乘。要求买彩色画的人数不少于某个值的方案,就可以转化成用总方案减去买彩色画的人数不足某个值的方案,于是大大地降低了数据范围。

对于 \(30\%\) 的数据可以直接动态规划。假如用 \(f_i\) 来代表了前 \(j-1\) 名顾客中有 \(i\) 名买了彩色画的方案数,用 \(a_j、b_j\) 来代表当前这个顾客最多的彩色画和黑白画的个数,有显而易见的转移方程是 \(f_i=f_i\times b_j+f_{i-1}\times a_j\) ,当然要注意判断边界和倒序枚举。

然后考场上的我就想出了一个傻逼的做法(这段内容不是正解),上面那个式子具有极强的矩阵乘法的特征,每个顾客的选择可以看成是如下的矩阵:

\[\begin{bmatrix}b_i&0&\dots&0\\a_i&b_i&\dots&0\\0&a_i&\dots&0\\\dots&\dots&\dots&\dots\\0&0&\dots&a_i\end{bmatrix} \]

于是就可以考虑建立一个线段树,每个叶子节点代表一个顾客的转移矩阵。这样一来就可以做到单点修改整体查询了。花了半个多小时打出来发现不论是时间还是空间都承受不住,然后然后这道题就只得了暴力分。果然我太弱了。

正解也用到了线段树,但并没有使用矩阵乘法。它考虑的是这个问题各部分互不干扰,符合分治的条件,也就是说对于要在区间 \([l,r]\) 中选择 \(x\) 个彩画顾客的方案会等于 \(ans(l,r,x)=\sum\limits_{i=0}^xans(l,mid,i)\times ans(mid+1,r,x-i)\) ,于是就可以直接上线段树了。由于这道题卡空间卡得比较死所以要用动态开点来建立线段树。

还有就是对于总方案数每次修改不需要重复计算,因为修改了乘积式中的一项就相当于是除以原来的值再乘上后来的值,用逆元即可。

代码:

#include<bits/stdc++.h>
//#define feyn
const int N=100010;
const int mod=10007;
using namespace std;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}

int m,n,num,a[N],b[N];

#define lc (t[wh].left)
#define rc (t[wh].right)
#define mid (l+r>>1)
struct node{
	int left,right,data[20];
}t[N<<1];
inline void pushup(int wh){
	for(int i=0;i<n;i++){
		t[wh].data[i]=0;
		for(int j=0;j<=i;j++){
			t[wh].data[i]+=t[lc].data[j]*t[rc].data[i-j]%mod;
			t[wh].data[i]%=mod;
		}
	}
}
int cnt=1;
inline int build(int wh,int l,int r){
	if(!wh)wh=++cnt;
	if(l==r){
		t[wh].data[0]=b[l];
		t[wh].data[1]=a[l];
		return wh;
	}
	lc=build(lc,l,mid);
	rc=build(rc,mid+1,r);
	pushup(wh);return wh;
}
inline void change(int wh,int l,int r,int pl){
	if(l==r){
		t[wh].data[0]=b[l];
		t[wh].data[1]=a[l];
		return;
	}
	if(pl<=mid)change(lc,l,mid,pl);
	else change(rc,mid+1,r,pl);
	pushup(wh);return;
}
#undef lc
#undef rc
#undef mid

inline int qpow(int s1,int s2){
	if(s2==1)return s1;
	int an=qpow(s1,s2>>1);
	if(s2&1)return an*an%mod*s1%mod;
	else return an*an%mod;
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=1;i<=m;i++)read(a[i]),a[i]%=mod;
	for(int i=1;i<=m;i++)read(b[i]),b[i]%=mod;
	build(1,1,m);int sum=1;
	for(int i=1;i<=m;i++)sum=sum*(a[i]+b[i])%mod;
	read(num);int s1,s2,s3;
	while(num--){
		read(s1);read(s2);read(s3);s2%=mod;s3%=mod;
		sum=sum*qpow(a[s1]+b[s1],mod-2)%mod;
		sum=sum*(s2+s3)%mod;
		a[s1]=s2;b[s1]=s3;
		change(1,1,m,s1);
		int ans=sum;
		for(int i=0;i<n;i++)ans-=t[1].data[i];
		printf("%d\n",(ans%mod+mod)%mod);
	}
	
	return 0;
}
posted @ 2022-07-18 17:25  Feyn618  阅读(33)  评论(0编辑  收藏  举报