暑假牛客多校第四场 2023-7-28(L、F、A、J)
L. We are the Lights
算法:反向构造
做法:
我们用c_on, r_on, c_off, r_off分别表示倒着推的行灯打开的集合、列灯打开的集合、列灯关闭的集合、行灯关闭的集合。倒着推时,我们先考虑on的情况。为了偷懒,我们就只考虑行的情况,因为列的情况实际上是一样的。
打开灯时,我们先查找r_on集合里是否之后要打开此行的灯,要打开就跳过,否则再找找r_off的集合中是否这行将被关闭,将被关闭也跳过。两种情况都不符合的话,我们就将这行加入r_on,并且加上这一行的最终打开的灯的数量,即ans += m - c_off.size() - c_on.size()。(注意:每一行要么出现在c_off,要么出现在c_on,因为如果这一行在从后往前推的过程中已经出现在了c_off里,那么现在你要打开这一行的灯是无意义的,因为未来你将关闭这行灯。如果这一行在从后往前推的过程中已经出现在了c_on里,那么现在你要关闭这一行的灯是无意义的,因为未来你要打开这一行的灯。)(另外,c_on.size()是为了防止重复加了某些灯。)
关闭灯时,我们先查找r_off集合里是否之后要关闭此行的灯,要关闭就跳过,否则再找找r_on的集合中是否这行将被打开,将被打开也跳过。两种情况都不符合的话,我们就将这行加入r_off,我们不需要删除任何数,因为我们再加灯的数量的时候就已经考虑了灯被关闭的影响。
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define fir first
#define sec second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 1000100;
int n, m, q;
struct W
{
string x, st;
int u;
}w[N];
void solve()
{
LL ans = 0;
cin >> n >> m >> q;
set<int> c_on, r_on, c_off, r_off;
for (int i = 0; i < q; i++)
{
int u;
string x, st;
cin >> x >> u >> st;
w[i] = { x, st, u };
}
for (int i = q - 1; i >= 0; i--)
{
int u = w[i].u;
string x = w[i].x, st = w[i].st;
if (st == "off")
{
if (x == "row" && r_off.find(u) == r_off.end() && r_on.find(u) == r_on.end())r_off.insert(u);
else if (x == "column" && c_off.find(u) == c_off.end() && c_on.find(u) == c_on.end())c_off.insert(u);
}
else if(st == "on")
{
if (x == "row")
{
if (r_off.find(u) != r_off.end())continue;
else if (r_on.find(u) != r_on.end())continue;
else
{
r_on.insert(u);
ans += m - c_off.size() - c_on.size();
}
}
else if(x == "column")
{
if (c_off.find(u) != c_off.end())continue;
else if (c_on.find(u) != c_on.end())continue;
else
{
c_on.insert(u);
ans += n - r_off.size() - r_on.size();
}
}
}
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}
F. Election of the King
算法:二分、sort
做法:我们知道对一个序列使用二分必须是这个序列具有二分性。那么这道题目的序列有没有二分性呢?一开始题目给定的序列并没有二分性,但是如果我们以这个人的值从小往大排并且相同的值按坐标从小往大排,我们再来研究这个序列。我们能研究出这个序列在[1, k]中的人是投票给最右边的人的,而在[k + 1, n]中都是投票给最左边的人的。那么怎么确定这个k呢?其实就是用到二分来确定,因为这一整段区间很明显已经具有二分性了。循环n - 1次,每次淘汰一个人,这个人要么是最左边的人,要么是最右边的人。(注意:,在左右边界的两个人若投票相同我们选择右边界的人。)
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 1000100;
int n;
PII w[N];
int sr(int a, int b)
{
int l = a, r = b;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (abs(w[mid].x - w[b].x) >= abs(w[mid].x - w[a].x))l = mid;
else r = mid - 1;
}
return l;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)cin >> w[i].x, w[i].y = i;
sort(w + 1, w + n + 1);
int l = 1, r = n;
for (int i = 1; i <= n - 1; i++)
{
int p = sr(l ,r);
if (p - l + 1 >= r - l + 1 - (p - l + 1))r--;
else l++;
}
cout << w[l].y;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}
A. Bobo String Construction
算法:kmp
做法:我们只需要构造全为0或全为1的串就可以了。当t全为0时,s全为1,当t全为1时,s全为0。其他情况我们只要把全为1和全为0的情况都考虑一遍再用kmp来判断就可以了。
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 1010;
int n, m;
int ne[N];
void solve()
{
string t;
cin >> n >> t;
bool all_one = true, all_zero = true;
for (int i = 0; i < t.size(); i++)
{
if (t[i] == '0')all_one = false;
if (t[i] == '1')all_zero = false;
}
string one = "", zero = "";
for (int i = 0; i < n; i++)one += '1', zero += '0';
if (all_one) { cout << zero << endl; return; }
else if (all_zero) { cout << one << endl; return; }
int cnt0 = 0, cnt1 = 0;
string ans0 = " " + t + zero + t, ans1 = " " + t + one + t, tt = " " + t;
for (int i = 2, j = 0; i < tt.size(); i++)
{
while (j && tt[j + 1] != tt[i])j = ne[j];
if (tt[j + 1] == tt[i])j++;
ne[i] = j;
}
m = t.size();
for (int i = 1, j = 0; i < ans0.size(); i++)
{
while (j && tt[j + 1] != ans0[i])j = ne[j];
if (tt[j + 1] == ans0[i])j++;
if (j == m)
{
cnt0++;
j = ne[j];
}
}
for (int i = 1, j = 0; i < ans1.size(); i++)
{
while (j && tt[j + 1] != ans1[i])j = ne[j];
if (tt[j + 1] == ans1[i])j++;
if (j == m)
{
cnt1++;
j = ne[j];
}
}
if (cnt0 == 2)cout << zero << endl;
else if (cnt1 == 2)cout << one << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t; cin >> t;
while (t--)
{
solve();
}
return 0;
}
J. Qu'est-ce Que C'est?
算法:动态规划,后缀数组
做法:
数组f[i][j]第一维表示有i个数,第二维表示最小后缀和为j( \(j\in[-m, m]\) )。
如果前i个数的最小前缀和大于等于0,设这个数为x,那么我们前i - 1的最小后缀和可以是[x - m, m],因为在[x - m, m]这个区间的任何一个数可以加上在区间[-m, m]中的某一个数最终使得最小后缀和为x。
如果前i个数的最小前缀和小于0,也设这个数为x,那么这个最小后缀和x一定是第i位所加的值k,即 \(k = x\) 。(这里简要说明一下为什么。如果前i - 1个数的最小后缀和为负数,那么下一个加的数必然得是正数,所以这种情况排除。如果前i - 1个数的最小后缀和大于等于0,那么我们一个负数加正数必然大于之前的负数,所以最下后缀和必然是这个所加的负数本身。注意我们不可能加上一个正数,因为加上正数且要求最小后缀和小于0,那么这个最小后缀和加上这个正数的区间是错误的,所以不可能加上正数。)那么前i - 1个数的最小前缀和与这个负数相加就应该大于等于0。
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };
const int N = 5010, mod = 998244353;
int n, m;
int f[2][2 * N], suf[2 * N];
void solve()
{
cin >> n >> m;
int p = 0;
for (int i = N + m; i >= N - m; i--)f[0][i] = 1;
for (int i = 2; i <= n; i++)
{
p = p ^ 1;
for (int i = N + m; i >= N - m; i--)suf[i] = ((LL)suf[i + 1] + f[p ^ 1][i]) % mod;
for (int x = 0; x <= m; x++)f[p][N + x] = suf[N - m + x];
for (int x = 1; x <= m; x++)f[p][N - x] = suf[N + x];
}
LL ans = 0;
for (int i = N - m; i <= N + m; i++)ans = (ans + f[p][i]) % mod;
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
while (t--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号