ABC288 EFG 题解

E
注意到后面选对前面的答案没有影响,而且前面选的顺序对后面的影响是连续的一段(如选 2 个,那么对应的 c 就应该是 c[i2..i](对应 i 是 1、2、3 个选时的答案))
然后就可以 dp 了:设 dp[i][j] 表示考虑到前 i 个物品,选了 j 个时的最小花费,转移分两种情况:

  • 选了第 i 个:dp[i][j]dp[i1][j1]+a[i]+minc[ij+1],..,c[i]
  • 没选(注意此时该物品不能在愿望单上) dp[i][j]dp[i1][j]
    答案就是 mindp[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]=j=0i1dp[j]×x[j+1..i]
然而直接转移是 O(n2) 的,需要优化
考虑将 x[j+1..i] 拆开 10x[j+1..i1]+x[i..i]
代入,dp[i]=j=0i2dp[j]×10x[j+1..i1]+x[i..i]j=0i1dp[j]
发现拆开之后恰好能用 dp[i1] 表示,即 dp[i]=10dp[i1]+x[i..i]j=0i1dp[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
因此相当于是做一个高级的差分,具体细节等之后再补充一下
UPD:image
现在相当于告诉你的 a[i] 是每一位符合 1,2,3 条件的 b 的前缀和,要求一个 b ,相当于 ab 的高维前缀和(注意这里不是二进制而是三进制)
做法就是 b[i,4]=a[i,2]a[i,3],b[i,5]=a[i,1]+a[i,3]a[i,2],b[i,6]=a[i,2]a[i,1]
枚举一下三进制位,每次在某一位为 0 的时候更新差分数组

// 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;
}
posted @   SkyRainWind  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示