RELATIVNOST (线段树优化DP)

RELATIVNOST (线段树优化DP)

题面

现在有n个人要买你的画,第i个人最多买ai个彩色的画,bi个黑白的画,你现在想要至少有c个人买了彩色的画,接下来有q个修改,每次修改某个人的ai和bi。问你每次修改之后有多少种情况可以满足你的要求。

分析

由于\(c\)很小,不妨补集转化,设\(f_{i,j}\)表示前\(i\)个人买了\(j\)张彩色的画的方案数。那么有\(f_{i,j}=b_if_{i-1,j} +a_if_{i-1,j-1}\).最终答案为\(\prod (a_i+b_i)-\sum_{j=0}^{c-1}f_{i-1,j}\)

容易发现转移和\(a_i,b_i\)的顺序无关,可以用线段树优化转移.我们重新定义子状态\(f_{x,j}\)表示子树内有\(j\)张彩色的画的方案数,那么转移就类似卷积的形式。

\[f_{x,i+j}=\sum f_{lson(x),i}\cdot f_{rson(x),j}(i+j \leq c) \]

边界条件\(f_{x,0}=b_l,f_{x,1}=a_l\),其中\(x\)是一个叶子节点\([l,l]\)

还有一个细节,我们要动态维护\((a_i+b_i)\)的乘积,那么修改时要乘上原来的\((a_i+b_i)\)逆元。但如果\((a_i+b_i) \bmod 10^4+7=0\)时不存在逆元,就无法维护乘积。因此还要额外记录一个这样的数的个数,然后记录其他数的乘积即可。另外,要避开这个分类讨论也可以直接把超过\(c\)的累加到\(f_{i,c}\)上,这样转移就变成了$$f_{x,\min(i+j,c)}=\sum f_{lson(x),i}\cdot f_{rson(x),j}$$,最后直接输出\(f_{1,c}\)即可,见第二份代码.

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 10007
#define maxn 100000
using namespace std;
int n,c,q;
int a[maxn+5],b[maxn+5];
inline int fast_pow(int x,int k){
	int ans=1;
	while(k){
		if(k&1) ans=ans*x%mod;
		x=x*x%mod;
		k>>=1;
	}
	return ans;
}
inline int inv(int x){
	return fast_pow(x,mod-2);
}
struct segment_tree{
	struct node{
		int l;
		int r;
		int dp[22];//dp[i][j]为彩色人数=j的方案数,再用总的减去 
	}tree[maxn*4+5];
	void push_up(int x){//dp与顺序无关,所以可以用线段树转移 
		for(int i=0;i<=c;i++) tree[x].dp[i]=0;
		for(int i=0;i<=c;i++){
			for(int j=0;j<=c;j++){
				if(i+j<=c) tree[x].dp[i+j]=(tree[x].dp[i+j]+tree[x<<1].dp[i]*tree[x<<1|1].dp[j]%mod)%mod;
			}
		}
	}
	void build(int l,int r,int x){
		tree[x].l=l;
		tree[x].r=r;
		if(l==r){
			tree[x].dp[0]=b[l]%mod;
			tree[x].dp[1]=a[l]%mod;
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,x<<1);
		build(mid+1,r,x<<1|1);
		push_up(x); 
	}
	void update(int upos,int x){
		if(tree[x].l==tree[x].r){
			tree[x].dp[0]=b[tree[x].l]%mod;
			tree[x].dp[1]=a[tree[x].l]%mod;
			return;
		}
		int mid=(tree[x].l+tree[x].r)>>1;
		if(upos<=mid) update(upos,x<<1);
		else update(upos,x<<1|1);
		push_up(x);
	}
}T;

int main(){
#ifndef LOCAL
	freopen("relati.in","r",stdin);
	freopen("relati.out","w",stdout);
#endif
	int p,x,y;
	scanf("%d %d",&n,&c);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	int sum=1,cnt=0;
	for(int i=1;i<=n;i++){
		if((a[i]+b[i])%mod==0) cnt++;//统计0的数量,因为不存在逆元 
		else sum=sum*(a[i]+b[i])%mod;
	}
	T.build(1,n,1);
	scanf("%d",&q);
	while(q--){
		scanf("%d %d %d",&p,&x,&y);
		if((a[p]+b[p])%mod==0) cnt--;
		else sum=sum*inv(a[p]+b[p])%mod;
		if((x+y)%mod==0) cnt++;
		else sum=sum*(x+y)%mod;
		a[p]=x;b[p]=y;
		T.update(p,1);
		int ans=0;
		for(int i=0;i<c;i++) ans=(ans+T.tree[1].dp[i])%mod;
		if(cnt>0) sum=0;
		printf("%d\n",(sum-ans+mod)%mod);
	}
} 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 10007
#define maxn 100000
using namespace std;
int n,c,q;
int a[maxn+5],b[maxn+5];
inline int fast_pow(int x,int k){
	int ans=1;
	while(k){
		if(k&1) ans=ans*x%mod;
		x=x*x%mod;
		k>>=1;
	}
	return ans;
}
inline int inv(int x){
	return fast_pow(x,mod-2);
}
struct segment_tree{
	struct node{
		int l;
		int r;
		int dp[22];//dp[i][j]为彩色人数=j的方案数,再用总的减去 
	}tree[maxn*4+5];
	void push_up(int x){//dp与顺序无关,所以可以用线段树转移 
		for(int i=0;i<=c;i++) tree[x].dp[i]=0;
		for(int i=0;i<=c;i++){
			for(int j=0;j<=c;j++){
				tree[x].dp[min(i+j,c)]=(tree[x].dp[min(i+j,c)]+tree[x<<1].dp[i]*tree[x<<1|1].dp[j]%mod)%mod;
			}
		}
	}
	void build(int l,int r,int x){
		tree[x].l=l;
		tree[x].r=r;
		if(l==r){
			tree[x].dp[0]=b[l]%mod;
			tree[x].dp[1]=a[l]%mod;
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,x<<1);
		build(mid+1,r,x<<1|1);
		push_up(x); 
	}
	void update(int upos,int x){
		if(tree[x].l==tree[x].r){
			tree[x].dp[0]=b[tree[x].l]%mod;
			tree[x].dp[1]=a[tree[x].l]%mod;
			return;
		}
		int mid=(tree[x].l+tree[x].r)>>1;
		if(upos<=mid) update(upos,x<<1);
		else update(upos,x<<1|1);
		push_up(x);
	}
}T;

int main(){
#ifndef LOCAL
	freopen("relati.in","r",stdin);
	freopen("relati.out","w",stdout);
#endif
	int p,x,y;
	scanf("%d %d",&n,&c);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	T.build(1,n,1);
	scanf("%d",&q);
	while(q--){
		scanf("%d %d %d",&p,&x,&y);
		a[p]=x;b[p]=y;
		T.update(p,1);
		printf("%d\n",T.tree[1].dp[c]);
	}
} 
posted @ 2020-10-20 15:23  birchtree  阅读(194)  评论(0编辑  收藏  举报