E
注意到后面选对前面的答案没有影响,而且前面选的顺序对后面的影响是连续的一段(如选 2 个,那么对应的 \(c\) 就应该是 \(c[i-2..i]\)(对应 \(i\) 是 1、2、3 个选时的答案))
然后就可以 dp 了:设 \(dp[i][j]\) 表示考虑到前 \(i\) 个物品,选了 \(j\) 个时的最小花费,转移分两种情况:
- 选了第 \(i\) 个:\(dp[i][j] \leftarrow dp[i-1][j-1]+a[i]+min{c[i-j+1], .., c[i]}\)
- 没选(注意此时该物品不能在愿望单上) \(dp[i][j] \leftarrow dp[i-1][j]\)
答案就是 \(min{dp[n][k..n]}\)
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;
int n,k,a[maxn],c[maxn],must[maxn];
ll dp[5005][5005];
signed main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&c[i]);
for(int i=1,x;i<=k;i++){
scanf("%d",&x);
must[x] = 1;
}
for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)dp[i][j] = 1e18;
dp[0][0] = 0;
for(int i=1;i<=n;i++){
ll cst = 1e18;
if(!must[i])dp[i][0] = dp[i-1][0];
for(int j=1;j<=i;j++){
cst = min(cst, 1ll * c[i - j + 1]);
dp[i][j] = min(dp[i][j], dp[i-1][j-1] + cst + a[i]);
if(!must[i])dp[i][j] = min(dp[i][j], dp[i-1][j]);
}
}
ll ans = 1e18;
for(int i=k;i<=n;i++)ans = min(ans, dp[n][i]);
cout << ans << '\n';
return 0;
}
F
设 \(dp[i]\) 表示考虑到第 \(i\) 位时的所有情况之和
\(dp[i] = \sum_{j=0}^{i-1}dp[j]\times x[j+1..i]\)
然而直接转移是 \(O(n^2)\) 的,需要优化
考虑将 \(x[j+1..i]\) 拆开 \(\rightarrow 10x[j+1..i-1] + x[i..i]\)
代入,\(dp[i] = \sum_{j=0}^{i-2}dp[j]\times 10x[j+1..i-1] + x[i..i]\sum_{j=0}^{i-1}dp[j]\)
发现拆开之后恰好能用 \(dp[i-1]\) 表示,即 \(dp[i] = 10dp[i-1] + x[i..i]\sum_{j=0}^{i-1}dp[j]\)
每次转移的时候维护一下 dp 的前缀和即可
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, mod = 998244353;
int n;
char s[200005];
ll dp,sdp;
signed main(){
scanf("%d",&n);
scanf("%s",s +1);
dp = s[1] - '0';sdp = dp+1; // +1(dp[0]=1)
for(int i=2;i<=n;i++){
int c = s[i]-'0';
dp = (dp*10%mod + sdp*c%mod) % mod;
(sdp += dp) %= mod;
}
cout << dp << '\n';
return 0;
}
G
注意到如果将三进制每一位拆开,题目中给的条件实际上相当于是一系列和
\(a[x]\) 第 \(i\) 位为 0,说明至少有 \(a[x]\) 个数,这一位为 0,1 (加起来)
为 1,说明这一位为 0,1,2
为 2,则为 1,2
因此相当于是做一个高级的差分,具体细节等之后再补充一下
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 1e6+5;
int n,p3[15],a[maxn];
signed main(){
scanf("%d",&n);
p3[0] = 1;
for(int i=1;i<=n;i++)p3[i] = 3*p3[i-1];
for(int i=0;i<p3[n];i++)scanf("%d",&a[i]);
for(int i=0;i<n;i++){
for(int j=0;j<p3[n];j++)if((j/p3[i])%3 == 0){
int v0=j, v1=j+p3[i], v2=j+2*p3[i];
int w0=a[v0], w1 = a[v1], w2 = a[v2];
a[v2]=w1-w0; a[v0]=w1-w2; a[v1]=w1-a[v2]-a[v0];
}
}
for(int i=0;i<p3[n];i++)printf("%d ",a[i]);
return 0;
}
标签:typedef,..,int,题解,long,ABC288,define,dp,EFG
From: https://www.cnblogs.com/SkyRainWind/p/17095068.html