• 题解 P1450 【[HAOI2008]硬币购物】


    题目链接

    Solution [HAOI2008]硬币购物

    题目大意:给定(4)种硬币的面值,多次询问,给定(4)种硬币的数目,求有多少种方法拼凑出面值(S)

    背包、容斥


    分析:

    先给出暴力做法(我一开始就是这么做的)

    类似生成函数,对于每种面值为(c),数量为(d)的硬币,我们将(x^c,x^{2c},x^{3c},cdots,x^{dc})的系数置为(1),暴力卷积四次即可

    单次(O(S^2))直接上天,NTT和FFT应该也是过不了的

    这个时候我们可以发挥一下人类智慧

    假定只有两种硬币,面值分别为(c_1,c_2),分别有(d_1,d_2)种,那么它们拼凑(S)的方案数,实际上就是不定方程(c_1x+c_2y=S)的满足(xin[0,d_1],yin[0,d_2])的解的个数

    这个是非常好算的,(ax+by=c),假如有特解(x_0,y_0),那么所有通解为(x=x_0+kfrac{b}{(a,b)},y=y_0-kfrac{a}{(a,b)}),列个不等式就可以算出(k)的个数即解的个数

    人类智慧算出前两个和后两个硬币的方案数,我们只需要求卷积后的第(S)项,那么可以(O(S))

    本机开(O2)可以(900ms)出结果,洛谷可以获得(60pts)好成绩,理论上循环展开之类的卡一下常数是可以跑过去的,代码附在最末


    然后正解,假设我们不考虑任何数量限制,那么这就是一个完全背包,可以直接(O(s))预处理出(f[n]),表示不考虑任何数量限制下凑出(n)面值的方案数

    然后我们减去非法方案,也就是第一个超限,第二个超限,一直到第四个超限,它们的方案并

    这个可以容斥原理

    如果钦定第一个超限,它的数量为(d),面值为(c),那么我们的方案数就是(f[S-c(d+1)]),其它同理

    正解:

    #include <iostream>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 100;
    int c[8],d[8],s,n;
    ll f[maxn] = {1ll};
    inline void solve(){
    	cin >> d[1] >> d[2] >> d[3] >> d[4] >> s;
    	ll ans = f[s];
    	for(int now = 1;now <= ((1 << 4) - 1);now++){
    		int flg = -1,tmp = s;
    		for(int i = 0;i < 4;i++)
    			if((now >> i) & 1){
    				flg *= -1;
    				tmp -= (d[i + 1] + 1) * c[i + 1];
    			}
    		if(tmp >= 0)ans += flg * f[tmp];
    	}
    	cout << ans << '
    ';
    }
    int main(){
    	cin >> c[1] >> c[2] >> c[3] >> c[4] >> n;
    	for(int i = 1;i <= 4;i++)
    		for(int j = c[i];j < maxn;j++)
    			f[j] += f[j - c[i]];
    	while(n--)solve();
    	return 0;
    }
    

    暴力:(貌似现在禁掉了指令集,不然AVX512是随便跑的/kk)

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e5 + 100;
    typedef long long ll;
    inline int exgcd(int a,int b,int &x,int &y){
    	if(!b){
    		x = 1,y = 0;
    		return a;
    	}
    	int res = exgcd(b,a % b,y,x);
    	y -= (a / b) * x;
    	return res;
    }
    inline int ceil1(int a,int b){return (a + b - 1) / b;}
    inline int floor1(int a,int b){return (a / b);}
    inline int fh(int x){return x >= 0 ? 1 : -1;}
    inline int abs(int x){return x >= 0 ? x : -x;}
    inline int ceil(int a,int b){
    	if(!a)return 0;
    	if(fh(a) * fh(b) == 1)return ceil1(a,b);
    	else return -floor1(abs(a),abs(b));
    }
    inline int floor(int a,int b){
    	if(!a)return 0;
    	if(fh(a) * fh(b) == 1)return floor1(a,b);
    	else return -ceil1(abs(a),abs(b));
    }
    ll ans;
    int c[8],d[8],a,b,n,s,x,y,dd,aa,bb;
    int tmp[4][maxn];
    inline int solve(int c,int d1,int d2){
    	int x = ::x,y = ::y,L = -0x7fffffff,R = 0x7fffffff;
    	if(c % dd)return 0;
    	x *= c / dd;
    	y *= c / dd;
    	L = max(L,ceil(-x,aa));
    	R = min(R,floor(d1 - x,aa));
    	if(L > R)return 0;
    	L = max(L,ceil(y - d2,bb));
    	R = min(R,floor(y,bb));
    	if(L > R)return 0;
    	return R - L + 1;
    }
    inline void solve(){
    	scanf("%d %d %d %d %d",d + 1,d + 2,d + 3,d + 4,&s);
    	ans = 0;
    	
    	a = c[1],b = c[2];
    	dd = exgcd(a,b,x,y),aa = b / dd,bb = (a / dd);
    	for(int i = 0;i <= s;i++)
    		if(d[1] * c[1] + d[2] * c[2] >= i)tmp[1][i] = solve(i,d[1],d[2]);
    		else tmp[1][i] = 0;
    	
    	a = c[3],b = c[4];
    	dd = exgcd(a,b,x,y),aa = b / dd,bb = (a / dd);
    	for(int i = 0;i <= s;i++)
    		if(d[3] * c[3] + d[4] * c[4] >= i)tmp[2][i] = solve(i,d[3],d[4]);
    		else tmp[2][i] = 0;
    	
    	for(int i = 0;i <= s;i++)
    		ans += tmp[1][i] * tmp[2][s - i];
    	printf("%lld
    ",ans);
    }
    int main(){
    	scanf("%d %d %d %d %d",c + 1,c + 2,c + 3,c + 4,&n);
    	while(n--)solve();
    	return 0;
    }
    
  • 相关阅读:
    WebContent的子目录里面的jsp文件无法将数据传递给Servlet
    MVC 与 三层架构
    使用命令行操作MySQL 及 语法
    JDBC
    字符典
    servlet 生命周期
    6 shell内置命令
    5shell中的数组
    4shell中的特殊变量
    3shell命令替换
  • 原文地址:https://www.cnblogs.com/colazcy/p/13788603.html
Copyright © 2020-2023  润新知