• 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) mod 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
    ",(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
    ",T.tree[1].dp[c]);
    	}
    } 
    
  • 相关阅读:
    使用react+html2canvas+jspdf实现生成pdf文件
    命名函数表达式
    java-信息安全(二十)国密算法 SM1,SM2,SM3,SM4
    003-docker-单宿主机下的网络模式
    【性能扫盲】性能测试面试题
    LoadRunner函数
    爬取干货集中营的美女图片
    ELK 性能优化实践 ---总结篇
    ELK 性能优化实践
    告警图片-搞笑的
  • 原文地址:https://www.cnblogs.com/birchtree/p/13846687.html
Copyright © 2020-2023  润新知