[HAOI2008]硬币购物

Description:

\ \ 方程$a_1 x_1+a_2x_2+a_3x_3+a_4x_4=s, $\ \ \(a_1\),\(a_2\),\(a_3\),\(a_4\)给定
\ \ 对于 \(q\)\(d_1\)~\(d_4\)\(s\) 求满足\(x_1<=d_1\), \(x_2<=d_2\), \(x_3<=d_3\), \(x_4<=d_4\)正整数解的个数

Hint:

\ \ \(d_i<=1e5\), \(s<=1e5\), \(q<=1e3\)

solution:

\ \ 多重背包显然超时
\ \ 考虑补集转化,求所有正整数解的个数-不满足di限制的个数
\ \ 设\(S_i\)\(x_i>d_i\)的解的个数,\(f[x]\)\(x\)时的正整数解
\ \ 即求\(S_1∪S_2∪S_3∪S_4=∑S_i-∑S_i∩S_j+∑S_i∩S_j∩S_k-\)......
\ \ 可以\(2^4\)枚举\(s-(d_i+1)*a_i\)......的\(f[]\)和,再用\(f[s]\)减去即可
\ \ 至于\(f[]\),一遍预处理即可

#include<bits/stdc++.h>
#define long long int
using namespace std;
const int mxn=1e5+5;
int f[mxn],a[5],d[5],t,s;

signed main()
{
	scanf("%d %d %d %d %d",&a[1],&a[2],&a[3],&a[4],&t);
	f[0]=1;
	for(int i=1;i<=4;++i) 
		for(int j=a[i];j<=mxn-5;++j) 
			f[j]+=f[j-a[i]];		//无限背包预处理
	for(int i=1;i<=t;++i) {
		scanf("%d %d %d %d %d",&d[1],&d[2],&d[3],&d[4],&s);
		int ans=0;
		for(int j=0;j<16;++j) { //枚举子集
			int cnt=0,tp=s;
			for(int k=0;k<4;++k) 
				if((j>>k)&1) ++cnt,tp-=(d[k+1]+1)*a[k+1];
			if(tp<0) continue ;	
			if(cnt&1) ans-=f[tp]; //容斥
			else ans+=f[tp];
		} 
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2019-02-11 13:02  cloud_9  阅读(104)  评论(0编辑  收藏  举报