atcoder beginner contest 159 Knapsack for All Segments

题目大意:

已知X[n]数列,定义f(L,R)为 X[i] 下标区间(L,R) 中部分数(即子集)求和为S的数量,求\sum _{1<=L<=N}\sum _{L<=R<=N} f(L,R)

N<=1e3,S<=1e3

解题思路:

我们假如已经知道区间(l,r)最左和最右的端点是满足题意的,那么根据乘法原理,这一小段区间对最后的答案所作的贡献就是:l*(n-r+1). 那么我们可以开始枚举r点,然后令dp[l][n]代表前l项中,和为n的最左的端点下标的和。所以,我们得到答案为:

dp[i-1][s-x[i]]*(n-i+1),其中i[1,n]

dp的转移为:

n>a[i],dp[i][n]=dp[i-1][n]+dp[i-1][n-a[i]]

首先dp[i-1][n]很好解释,就是本次第i项对dp不作贡献。dp[i-1][n-a[i]]代表就是第i项为求和做贡献了但是注意这里我们不需要加上i,因为第i项不会是最左的下标。

n=a[i],dp[i][n]=i.

这里就是第i项直接作贡献的情况。

这里有一些学习的点。首先我们这里不能用简单的dp的思维直接套状态比如:dp[l][r]得出这段区间的求和数。需要用到贡献法的思维,这里是从一小段区间往外推求得的贡献。另外这里,为什么可以枚举r端点呢,主要是因为左边的下标的和是可以转移的!

#include <bits/stdc++.h>
#define int long long 
const int MODN=998244353;
const int MAXN=3010;
using namespace std;
 
int dp[2][MAXN];
//滚动数组
int32_t main(){
    dp[1][0]=1;
    int cur=1;
    int n,k;cin>>n>>k;
    vector<int> arrmv(n+1);
    for(int i=0;i<n;i++)cin>>arrmv[i+1];
    vector<int> a=arrmv;
    int ans=0;
    for(int i=1;i<=n;i++){
        if(k-a[i]>=0){
            if(k-a[i]>0)ans+=dp[cur][k-a[i]]*(n-i+1);
            else ans+=i*(n-i+1);
                ans%=MODN;
        }
        cur=!cur;
        for(int j=k;j>=0;j--){
            dp[cur][j]=0;
            dp[cur][j]+=dp[!cur][j];
            if(j-a[i]>0)dp[cur][j]+=dp[!cur][j-a[i]];
            else if(j==a[i])dp[cur][j]+=i;

            dp[cur][j]%=MODN;
        }
    }
    cout<<ans<<endl;
	return 0;
}
 
//无滚动
// #include <bits/stdc++.h>
// #define int long long 
// const int MODN=998244353;
// const int MAXN=3010;
// using namespace std;
 
// int dp[MAXN][MAXN];
// int32_t main(){
    // dp[0][0]=1;
    // int n,k;cin>>n>>k;
    // vector<int> arrmv(n+1);
    // for(int i=0;i<n;i++)cin>>arrmv[i+1];
    // vector<int> a=arrmv;
    // int ans=0;
    // for(int i=1;i<=n;i++){
        // if(k-a[i]>=0){
            // if(k-a[i]>0)ans+=dp[i-1][k-a[i]]*(n-i+1);
            // else ans+=i*(n-i+1);
                // ans%=MODN;
        // }
        // for(int j=k;j>=0;j--){
            // dp[i][j]+=dp[i-1][j];
            // if(j-a[i]>0)dp[i][j]+=dp[i-1][j-a[i]];
            // else if(j==a[i])dp[i][j]+=i;
            // dp[i][j]%=MODN;
            // cout<<dp[i][j]<<" ";
        // }
        // cout<<endl;
    // }
    // cout<<ans<<endl;
	// return 0;
// }
发布了171 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/FrostMonarch/article/details/105124193
今日推荐