区域赛网络赛上海站

Stone game

题目链接
题意:有n个石头,让你选出来一堆,使得你选出来的这一堆的质量大于等于剩下的质量,并且将你选的这一堆中任意一个石子拿走,这一堆的质量就小于等于剩下的质量。
思路:刚刚学的退背包思想。将石子按从小到大排序,dp[k]表示取重量为k的方案数,这就是普通的0-1背包。对于a[i],我们将以a[i]为最小值并满足条件的方案加上。所以我们将选a[i]的情况减掉,然后再一层for判断如果满足条件,则将这种方案数加上。

#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f

using namespace std;
const int maxn=2e5+5;
const int mod=1e9+7;

int n,a[305];
LL dp[maxn];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)
        {
            for(int j=sum;j>=a[i];j--)
            {
               dp[j]=(dp[j]+dp[j-a[i]])%mod;
            }
        }
        LL ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=a[i];j<=sum;j++)
            {
                dp[j]=(dp[j]-dp[j-a[i]]+mod)%mod;///将选a[i]的情况给减掉
            }
            for(int j=0;j<=sum;j++)
            {
                if(j+a[i]>=sum-j-a[i]&&j<=sum-j-a[i])ans=(ans+dp[j])%mod;
            }
        }
        printf("%lld\n",ans);
    }
}

D. Counting Sequences I

题目链接
题意:问长度为n的序列,满足序列的和等于乘积,的序列有多少种。
思路:暴力搜索,应用多重排列公式 n!/(k1!k2!…ki!),k为重复的数的个数,计算方案数。

#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f

using namespace std;
const int maxn=3005;
const int mod=1e9+7;

int ans[maxn],n;
LL f[maxn],inv[maxn];

LL qpow(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1)ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}

void init()
{
    f[0]=1;
    //inv[0]=1;
    for(int i=1;i<=3000;i++)
    {
        f[i]=(f[i-1]*i)%mod;
    }
    for(int i=0;i<=3000;i++)inv[i]=qpow(f[i],mod-2);
}

LL dfs(int x,int num,int mul,int sum,int cnt,LL INV)///x是当前乘的数,num是不等于1的个数,mul是乘积的结果,sum是加和的结果,cnt是重复的数的个数
{
    if(n==num)///如果n个数都用完了
    {
        if(mul==sum)return (((f[n]*inv[cnt])%mod)*INV)%mod;
        return 0;
    }
    if(mul==sum+n-num)///如果当前满足条件,就将此方案加上去
    {
        return (((f[n]*INV)%mod)*((inv[cnt]*inv[n-num])%mod))%mod;
    }
    if(x>n||mul>sum+n-num||mul*x>sum+x+n-num-1)return 0;///剪枝
    LL ans=0;
    ans+=dfs(x+1,num,mul,sum,0,INV*inv[cnt]%mod)%mod;
    ans+=dfs(x,num+1,mul*x,sum+x,cnt+1,INV)%mod;
    return ans%mod;
}

int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        printf("%lld\n",dfs(2,0,1,0,0,1));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值