牛客周赛 Round 102题解

D、小红的华撃串

题意:

将给定的01串通过翻转某些字符,将字符串变为恰好包含三个“01”或“10”子串,且翻转次数最小。

思路:

可知最终的字符串一定形如 0101或者 1010。(每个字符可以有多个,例如00001000011)。
故考虑dp
dp[i][j]表示第i个序列分到第j块,最终的字符串形如0101所需要的最小翻转次数
dp2[i][j]表示第i个序列分到第j块,最终的字符串形如1010所需要的最小翻转次数

又或者:
使用三维动态规划数组 dp[i][j][k],其中:
i:处理到第 i 个字符(0-indexed)。
j:当前段数(1到4)。
k:当前字符是0或1。
状态值表示达到该状态的最小翻转次数。

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;

void solve() {
	 int n;
	 cin >> n;
	 string s;
	 cin >> s;
	 s = ' ' + s;
	 vector<vector<int> > dp (n + 1, vector<int> (4, inf));
	 vector<vector<int> > dp2 (n + 1, vector<int> (4, inf));
	 dp[0][0] = 0;
	 dp2[0][0] = 0;
	 for (int i = 1; i <= n; ++i) {
	 	 if (s[i] == '1') {
	 	 	 dp[i][0] = dp[i - 1][0];
	 	 	 if (i >= 2) dp[i][1] = min (dp[i - 1][1], dp[i - 1][0]) + 1;
	 	 	 if (i >= 3) dp[i][2] = min (dp[i - 1][2], dp[i - 1][1]);
	 	 	 if (i >= 4) dp[i][3] = min (dp[i - 1][3], dp[i - 1][2]) + 1;
	 	 }else if (s[i] == '0') {
	 	 	 dp[i][0] = dp[i - 1][0] + 1;
	 	 	 if (i >= 2) dp[i][1] = min (dp[i - 1][1], dp[i - 1][0]);
	 	 	 if (i >= 3) dp[i][2] = min (dp[i - 1][1], dp[i - 1][2]) + 1;
	 	 	 if (i >= 4) dp[i][3] = min (dp[i - 1][2], dp[i - 1][3]);
	 	 }
	 }
	 for (int i = 1; i <= n; ++i) {
	 	 if (s[i] == '0') {
	 	 	 dp2[i][0] = dp2[i - 1][0];
	 	 	 if (i >= 2) dp2[i][1] = min (dp2[i - 1][1], dp2[i - 1][0]) + 1;
	 	 	 if (i >= 3) dp2[i][2] = min (dp2[i - 1][1], dp2[i - 1][2]);
	 	 	 if (i >= 4) dp2[i][3] = min (dp2[i - 1][2], dp2[i - 1][3]) + 1;
	 	 }else if (s[i] == '1') {
	 	 	 dp2[i][0] = dp2[i - 1][0] + 1;
	 	 	 if (i >= 2) dp2[i][1] = min (dp2[i - 1][1], dp2[i - 1][0]);
	 	 	 if (i >= 3) dp2[i][2] = min (dp2[i - 1][2], dp2[i - 1][1]) + 1;
	 	 	 if (i >= 4) dp2[i][3] = min (dp2[i - 1][3], dp2[i - 1][2]);
	 	 }
	 }
	 cout << min(dp[n][3], dp2[n][3]) << "\n";
}
int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 //cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}

E、小红的01串(easy)

题意:

求出使得贡献为k的,序列的最小长度,其中贡献为\(\frac{len \times (len + 1)}{2}\)\(len\)为连续'1'的长度

思路:

长度为value, 贡献为wight, 考虑完全背包

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int N = 640 + 10;

int w[N];
int v[N];

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
vector<int> dp (2e5 + 10, inf);
void init () {
	 for (int i = 1; i < N; ++i) {
	 	 w[i] = (i + 1) * i / 2;
	 	 v[i] = i;
	 }
	 dp[0] = 0;
     for (int i = 1; i < N; ++i) {
     	 for (int j = w[i]; j <= 2e5 + 9; ++j) {
     	 	 dp[j] = min (dp[j], dp[j - w[i]] + (v[i] + 1));
     	 }
     }
}
void solve() {
	 int k;
	 cin >> k;
     cout << dp[k] -  1 << "\n";
}
int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 init ();
	 cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}

F、小红的01串(hard)

题意:

在上一题的基础上,给出构造的序列

思路:

在dp的时候,记录当前wight是从哪里转移过来的

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int N = 640 + 10;
const int M = 2e5 + 10;

int w[N];
int v[N];
int pre[M];

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
int k;
vector<int> dp (2e5 + 10, inf);
void init () {
	 for (int i = 1; i < N; ++i) {
	 	 w[i] = (i + 1) * i / 2;
	 	 v[i] = i;
	 }
	 dp[0] = 0;
     for (int i = 1; i < N; ++i) {
     	 for (int j = w[i]; j <= 2e5 + 9; ++j) {
     	 	 if (dp[j] > dp[j - w[i]] + v[i] + 1) {
     	 	 	 dp[j] = dp[j - w[i]] + v[i] + 1;
     	 	 	 pre[j] = i;
     	 	 }
     	 }
     }
}


void solve() {
	 cin >> k;
	 vector<int> ans;
     while (k > 0) {
     	 ans.push_back (pre[k]);
     	 k -= w[pre[k]];
     }
     int nn = ans.size ();
     for (int i = 0; i < nn; ++i) {
     	 if (i == nn - 1) {
     	 	 for (int j = 1; j <= ans[i]; ++j) {
     	 	 	 cout << "1";
     	 	 }
     	 }else{
     	 	 for (int j = 1; j <= ans[i]; ++j) {
     	 	 	 cout << "1";
     	 	 }cout << "0";
     	 }
     }
     cout << "\n";
     return ;
}
int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 init ();
	 cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}

G、小红的双排列查询

题意:

小红拿到了一个长为 $ n $ 的数组 \(\{a_1, a_2, \ldots, a_n\}\)

小芳对双排列很感兴趣,向小红提出了 $ q $ 个问题,每个问题由两个整数 $ l, r (1 \leq l < r \leq n) $ 组成,需要回答:

  • 子数组 \(\{a_l, a_{l+1}, \ldots, a_r\}\) 是否为双排列。

小红被难住了,请你帮帮他。

【名词解释】

双排列:长度为 $ 2 \times n $ 的双排列为两个长度为 $ n $ 的排列打乱顺序后得到的数组。

排列:长度为 $ n $ 的排列是由 $ 1, 2, \ldots, n $ 这 $ n $ 个整数、按任意顺序组成的数组(每个整数均恰好出现一次)。例如,\(\{2, 3, 1, 5, 4\}\) 是一个长度为 5 的排列,而 \(\{1, 2, 2\}\)\(\{1, 3, 4\}\) 都不是排列,因为前者存在重复元素,后者包含了超出范围的数。

思路:

\(mul(l, r) == targetmul, sum (l, r) == targetsum\)即可

代码

#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;

const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
const int mod = 998244353;
const int N = 3e5 + 10;

//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
int a[N];

struct node {
	 ll l, r, mul;
};
node tr[N * 4];
ll S[N];

void pushup (int u) {
	 tr[u].mul = (tr[u << 1].mul * tr[u << 1 | 1].mul) % mod;
}
void build (int u, int l, int r) {
	 tr[u] = {l, r};
	 if (l == r) {
	 	 tr[u] = {l, r, a[l]};
	 }else{
	 	 int mid = l + r >> 1;
	 	 build (u << 1, l, mid);
	 	 build (u << 1 | 1, mid + 1, r);
	 	 pushup (u);
	 }
}

void init () {
	 S[0] = 1;
	 for (int i = 1; i < N; ++i) {
	 	 S[i] = S[i - 1] * i % mod;
	 }
}

ll query (int u, int l, int r) {
	 if (tr[u].l >= l && tr[u].r <= r) {
	 	 return tr[u].mul;
	 }else{
	 	 int mid = tr[u].l + tr[u].r >> 1;
	 	 ll temp = 1;
	 	 if (l <= mid) {
	 	 	 temp = temp * query (u << 1, l, r) % mod;
	 	 }
	 	 if (r > mid) {
	 	 	 temp = temp * query (u << 1 | 1, l, r) % mod;
	 	 }
	 	 return temp;
	 }
}
void solve() {
	 int n, m;
	 cin >> n >> m;
	 vector<ll> b (n + 1);
	 for (int i = 1; i <= n; ++i) {
	 	 cin >> a[i];
	 	 b[i] = (b[i - 1] + a[i]) % mod;
	 }
	 build (1, 1, n);
	 while (m --) {
	 	int l, r;
	 	cin >> l >> r;
	 	ll len = r - l + 1;
	 	if (len & 1) {
	 		 cout <<"No" <<"\n";
	 		 continue;
	 	}
	 	ll sumtarget = (len * len / 4 + len / 2) % mod;
	    ll multarget = S[len / 2] * S[len / 2] % mod;
	    if ((b[r] - b[l - 1] +  mod) % mod == sumtarget && query (1, l, r) == multarget) {
	    	 cout << "Yes" << "\n";
	    }else{
	    	 cout << "No" << "\n";
	    }
	 }
}

int main() {
	 ios::sync_with_stdio (false);
	 cin.tie(NULL);
	 cout.tie(NULL);
	 t = 1;
	 init ();
	 //cin >> t;
	 while (t --) {
	 	 solve();
	 }
	 return 0;
}
posted @ 2025-08-03 03:08  Li_Yujia  阅读(15)  评论(0)    收藏  举报