焦作网络赛(今天开始ACM康复计划~)

本文探讨了背包问题的一种特殊情形,即如何计算装载特定数量货物的方法数,并使用矩阵快速幂解决复杂递推关系的问题。文章提供了详细的代码实现,包括背包问题的C++代码和矩阵快速幂的超短版实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目在计蒜课上

K:Transport Ship

给n种船,每个船分别可以装2^n-1个货物,有S个货物,问装S个货物就几种装配方法。

开始想的多元线性回归,然后想的暴力,脑子真的蠢,这么裸的背包没一眼看出,补补补补补补补补!!

代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const long long maxn = 1e5+7;
    const int mod=1e9+7;
    struct ttt{
        int v,num;
    };
    ttt q1[maxn];
    int res[maxn];
    int main(){
        //freopen("in.txt","r",stdin);
        //freopen("out1.txt","w",stdout);
        int i,j,k,f1,f2,f3,t1,t2,t3;
        int T;
        int n,m;
        int l;
        scanf("%d",&T);
        while(T--){
        scanf("%d %d",&n,&m);
        memset(res,0,sizeof(res));
        for(i=1;i<=n;i++){
        scanf("%d %d",&q1[i].v,&q1[i].num);
        }
        res[0]=1;
        for(i=1;i<=n;i++){
            for(j=0;j<q1[i].num;j++){ //因为是2^k-1个,所以取的时候是1/2/4/8/2^(k-1)刚好全部覆盖
                for(k=10000;k>=q1[i].v;k--){
                    if(k-(1<<j)*q1[i].v<0)break;
                    res[k]+=res[k-(1<<j)*q1[i].v];
                    res[k]%=mod;
                }
            }
        }

        for(i=1;i<=m;i++){
        scanf("%d",&t1);
    //    cout << t1<< endl;
        printf("%d\n",res[t1]);
        }
        }
        return 0;
    }

L:矩阵快速幂(从黄大佬手里摸了一套超短的快速幂板子

自己找一个递推公式。

本来一般矩阵快速幂是f[n]=2f[n-1]+3f[n-2]+5f[n-5]的形式,

现在改为f[n]=2g(n-1)+h(n-1)

但是每个g和h又能通过前面的函数推出,那么这样子也可以矩阵快速幂列出矩阵

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const long long maxn = 1e5+7;
const int mod=1e9+7;
const int N = 9;
struct Mat{
	ll m[N][N];
	void clear(){memset(m,0,sizeof(m));}
	void I(){for (int i=0;i<N;i++) m[i][i]=1;}
};
Mat mul(const Mat &a, const Mat &b){
	Mat c;c.clear();
	for (int i=0;i<N;i++)
		for (int j=0;j<N;j++){
			for (int k=0;k<N;k++)
				c.m[i][j]+=a.m[i][k]*b.m[k][j]%mod;
			c.m[i][j]%=mod;
		}
	return c;
}
Mat qpow(Mat a, ll x){ //这里注意传进来的参数要为long long 不然会报超时
	Mat ans; ans.clear(); ans.I();
	for (;x;a=mul(a,a),x>>=1)
		if (x&1)
			ans=mul(ans,a);
	return ans;
}
Mat t2=(Mat){{
{0,0,0,1,0,0,1,0,0},
{1,0,0,1,0,0,1,0,0},
{1,0,0,1,0,0,0,0,0},
{0,1,0,0,1,0,0,1,0},
{0,1,0,0,0,0,0,1,0},
{0,1,0,0,1,0,0,0,0},
{0,0,1,0,0,0,0,0,1},
{0,0,0,0,0,1,0,0,1},
{0,0,1,0,0,1,0,0,0}
}};

int main(){
        //freopen("in.txt","r",stdin);
        //freopen("out1.txt","w",stdout);
        int i,j,k,f1,f2,f3;
        int T;
        int l;
        ll t1;
        scanf("%d",&T);
        while(T--){
        scanf("%lld",&t1);
        if(t1==1){
        printf("3\n");continue;
        }else if(t1==2){
        printf("9\n");continue;
        }
        Mat t3;
        t3=qpow(t2,t1-2);
        int ans=0;
        for(i=0;i<9;i++)
            for(j=0;j<9;j++){
                ans+=t3.m[i][j];
                ans%=mod;
            }
        printf("%d\n",ans);
        }
        return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值