AtCoder Beginner Contest 381(A—F)(字符串哈希,二分,状压dp)

比赛链接

AtCoder Beginner Contest 381

A题

代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(time(0));

int n;
string s;
void solve(int test_case)
{
    cin >> n >> s;
    bool ok = true;
    if (n % 2 == 0)
    {
        ok = false;
    }
    for (int i = 0; i < n / 2; i++)
    {
        if (s[i] != '1')
            ok = false;
    }
    if (s[n / 2] != '/')
        ok = false;
    for (int i = n / 2 + 1; i < n; i++)
    {
        if (s[i] != '2')
            ok = false;
    }
    if (ok)
    {
        cout << "Yes" << endl;
    }
    else
        cout << "No" << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int test = 1;
    // cin >> test;
    for (int i = 1; i <= test; i++)
    {
        solve(i);
    }
    return 0;
}

B题

代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(time(0));

int n;
string s;
void solve(int test_case)
{
    cin >> s;
    n = s.size();
    bool ok = true;
    if (n & 1)
        ok = false;
    map<char, int> mp;
    for (char c : s)
        mp[c]++;
    for (int i = 1; i < n; i += 2)
    {
        if (s[i] != s[i - 1])
            ok = false;
    }
    for (auto mpp : mp)
    {
        if (mpp.second != 2)
            ok = false;
    }
    if (ok)
    {
        cout << "Yes" << endl;
    }
    else
        cout << "No" << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int test = 1;
    // cin >> test;
    for (int i = 1; i <= test; i++)
    {
        solve(i);
    }
    return 0;
}

C题

思路

对于 11 / 22 11/22 11/22字符串,我们考虑二分“/”左边 1 1 1的个数 x x x

枚举原串中的每一个 / / /,如果其左边的长度为 x x x的子串全为 1 1 1,右边的长度为 x x x的子串全为 2 2 2,则为true,否则为false。

对于字符串的匹配问题,我们可以考虑使用字符串哈希,这样可以使判断区间字符串是否相等的时间复杂度将为 O ( 1 ) O(1) O(1)

代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(time(0));

int n;
char s[N];
// 双模哈希
const u64 base1 = 19260817, base2 = 1004535809;
int a[N], b[N];
int ha1[N], fac1[N], ha2[N], fac2[N], ha3[N], fac3[N];
void Hash()
{
    fac1[0] = fac2[0] = fac3[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        fac1[i] = fac1[i - 1] * base1 % base2;
        fac2[i] = fac2[i - 1] * base1 % base2;
        fac3[i] = fac3[i - 1] * base1 % base2;
    }
    for (int i = 1; i <= n; i++)
    {
        ha1[i] = (ha1[i - 1] + a[i] * fac1[n - i] % base2) % base2;
        ha2[i] = (ha2[i - 1] + b[i] * fac2[n - i] % base2) % base2;
        ha3[i] = (ha3[i - 1] + (int)(s[i]) * fac3[n - i] % base2) % base2;
    }
}
int gethash1(int l, int r)
{
    return (ha1[r] - ha1[l - 1] + base2) * fac1[l] % base2;
}
int gethash2(int l, int r)
{
    return (ha2[r] - ha2[l - 1] + base2) * fac2[l] % base2;
}
int gethash3(int l, int r)
{
    return (ha3[r] - ha3[l - 1] + base2) * fac3[l] % base2;
}
bool check(int x)
{
    bool ok = false;
    for (int i = 1; i <= n; i++)
    {
        if (s[i] == '/')
        {
            int L = i - x;
            int R = i + x;
            if (L < 1)
                continue;
            if (R > n)
                continue;
            if (gethash1(L, i - 1) == gethash3(L, i - 1) && gethash2(i + 1, R) == gethash3(i + 1, R))
                ok = true;
        }
    }
    return ok;
}
void solve(int test_case)
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> s[i];
        a[i] = '1';
        b[i] = '2';
    }
    Hash();
    int low = 0, high = n;
    while (low < high)
    {
        int mid = low + high + 1 >> 1;
        if (check(mid))
        {
            low = mid;
        }
        else
            high = mid - 1;
    }
    cout << low * 2 + 1 << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int test = 1;
    // cin >> test;
    for (int i = 1; i <= test; i++)
    {
        solve(i);
    }
    return 0;
}

D题

思路

因为 X X X的长度为偶数,则其开头的第一个字符,在原串中的位置要么是奇数要么是偶数

我们可以分别以奇数开头和以偶数开头,对两个相邻的字符进行合并。如果相邻的两个字符相等,则合并为一个,如果不相等,则合并成一个 − 1 -1 1

最后对于每一个被 − 1 -1 1所隔开的区间,用双指针求出不包含重复数字的区间最大长度即可。

代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 3e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(time(0));

int n;
int a[N], cnt[N];
int func(vector<int> &v)
{
	int L = 0;
	int sum = 0;
	for (int R = 0; R < v.size(); R++)
	{
		cnt[v[R]]++;
		while (cnt[v[R]] > 1)
			cnt[v[L++]]--;
		sum = max(sum, R - L + 1);
	}
	while (L < v.size())
		cnt[v[L++]]--;
	return sum * 2;
}
void solve(int test_case)
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	int ans = 0;
	for (int j = 2; j <= 3; j++)
	{
		vector<int> v;
		for (int i = j; i <= n; i += 2)
		{
			if (a[i] == a[i - 1])
			{
				v.push_back(a[i]);
			}
			else
			{
				ans = max(ans, func(v));
				v.clear();
			}
		}
		ans = max(ans, func(v));
	}
	cout << ans << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int test = 1;
	// cin >> test;
	for (int i = 1; i <= test; i++)
	{
		solve(i);
	}
	return 0;
}

E题

思路

对于每一次查询的区间 [ L , R ] [L,R] [L,R],满足条件的子序列的长度,一定是一个凸函数。

因此我们可以使用前缀和对 1 1 1 2 2 2进行预处理,使得枚举每一个"/"时,查询长度的时间复杂度都可以降至 O ( 1 ) O(1) O(1)

对于求凸函数的最大值,我们使用二分查找即可。

代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 1e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(time(0));

int n, q;
int a[N], b[N], nxt[N];
char s[N];
void solve(int test_case)
{
	cin >> n >> q;
	for (int i = 1; i <= n; i++)
	{
		cin >> s[i];
	}
	for (int i = 1; i <= n; i++)
	{
		a[i] = a[i - 1];
		b[i] = b[i - 1];
		if (s[i] == '1') a[i]++;
		if (s[i] == '2') b[i]++;
	}
	nxt[n + 1] = n + 1;
	for (int i = n; i >= 1; i--)
	{
		nxt[i] = (s[i] == '/' ? i : nxt[i + 1]);
	}
	while (q--)
	{
		int l, r;
		cin >> l >> r;
		int k = nxt[l];
		if (k > r)
		{
			cout << 0 << endl;
			continue;
		}
		int low = l, high = r;
		while (low < high)
		{
			int mid = low + high + 1 >> 1;
			int k = nxt[mid];
			if (k > r)
			{
				high = mid - 1;
				continue;
			}
			if (a[k] - a[l - 1] <= b[r] - b[k])
			{
				low = mid;
			}
			else high = mid - 1;
		}

		int ans = min(a[nxt[low]] - a[l - 1], b[r] - b[nxt[low]]);

		if (nxt[low + 1] <= r)
		{
			low = nxt[low + 1];
			ans = max(ans, min(a[low] - a[l - 1], b[r] - b[low]));
		}

		cout << ans * 2 + 1 << endl;
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int test = 1;
	// cin >> test;
	for (int i = 1; i <= test; i++)
	{
		solve(i);
	}
	return 0;
}

F题

思路

我们考虑使用二进制状压dp。

d p [ S ] dp[S] dp[S]表示取完集合 { S } \{S\} {S}内的所有元素所需要的最短前缀。

转移时枚举当前第 i i i个存在在集合中的元素,考虑从 d p [ S ⊕ 1 < < i ] dp[S \oplus 1 << i] dp[S1<<i]进行转移。

我们可以使用二分查找,找到第一个不被 d p [ S ⊕ 1 < < i ] dp[S \oplus 1 << i] dp[S1<<i]覆盖的 i i i的位置,然后转移即可。

代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(time(0));

int n;
int a[N], dp[1ll << 20 + 5];
vector<int>pos[20];
void solve(int test_case)
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		a[i]--;
		pos[a[i]].push_back(i);
	}
	fill(dp, dp + (1ll << 20) + 5, inf);
	dp[0] = 0;
	int ans =  0;
	for (int val = 1; val < (1ll << 20); val++)
	{
		for (int i = 0; i < 20; i++)
		{
			if ((val >> i & 1) && dp[val ^ (1ll << i)] != inf)
			{
				int idx = upper_bound(pos[i].begin(), pos[i].end(), dp[val ^ (1ll << i)]) - pos[i].begin();
				if (idx + 1 < pos[i].size())//因为每一次要加两个字符,所以是idx+1
				{
					dp[val] = min(dp[val], pos[i][idx + 1]);
				}
			}
		}
		if (dp[val] != inf)
		{
			ans = max(ans, (int)__builtin_popcount(val) * 2);
		}
	}
	cout << ans << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int test = 1;
	// cin >> test;
	for (int i = 1; i <= test; i++)
	{
		solve(i);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值