Codeforces Round #848 (Div. 2),C. Flexible String
思路:
字符串a有cnt个不同字母(最多有10个不同字母,不是26个),而我们需要取num=min(k,cnt)种字母来取代,最大情况是C(5,10)=252种组合,所以我们完全可以dfs暴力所有组合。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
typedef pair<int, int> pii;
//double 型memset最大127,最小128
//std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
const int INF = 0x3f3f3f3f; //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 1e5 + 10;
int n, k, cnt, x[30], num;
ll ans;
bool vis[30];
string a, b;
set<int>s;
void dfs(int i1, int cnt1)
{
if (i1 > cnt)
{
if (cnt1 == num)//当选取的数对于num时
{
ll sum = 0, tmp = 0;
for (int i = 0; i < n; ++i)
{
if (a[i] == b[i] || vis[a[i] - 'a'])sum++;
else tmp += (sum) * (sum + 1) / 2, sum = 0;
}
tmp += sum * (sum + 1) / 2;
ans = max(tmp, ans);
}
}
else
{
dfs(i1 + 1, cnt1);
vis[x[i1]] = 1;//标记x数组的这个数选了
if (cnt1 < num)dfs(i1 + 1, cnt1 + 1);//只有选取的数小于需要的数量时才选
vis[x[i1]] = 0;
}
}
int main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
{
memset(vis, 0, sizeof(vis));
s.clear();
cin >> n >> k >> a >> b;
cnt = 0;
for (int i = 0; i < n; ++i)
{
int tmp = a[i] - 'a';
if (!s.count(tmp))x[++cnt] = tmp, s.insert(tmp);//记录a中不同的字母数
}
ans = 0;
num = min(k, cnt);
dfs(1, 0);
cout << ans << endl;
}
return 0;
}
Codeforces Round #849 (Div. 4)G2. Teleporters (Hard Version)
思路:
1,我们每一步都希望走最小的点,所以每个点都取min(a[i]+i,a[i]+n-i+1)。
2,但是,第一步只能从0出发,即第一步只能取a[i]+i,显然我们需要遍历所有第一步,之后的步数就是对除了第一步来到的点之外的点尽可能多的走,我们会发现,每次遍历第一步,后面步数基本是重复剩下几个步数小的点。
3,所以我们可以把所以点的最小步数排序(同时记录他的第一步的步数),计算前缀和,这样我们二分答案即可,如果第一步的点在选择的前几个点里面,减去即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
typedef pair<int, int> pii;
//double 型memset最大127,最小128
//std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
const int INF = 0x3f3f3f3f; //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 2e5 + 10;
pair<int, int>a[N];
ll sum[N];
int main()
{
int t, n, x, c;
cin >> t;
while (t--)
{
cin >> n >> c;
int ans = 0;
for (int i = 1; i <= n; ++i)
{
cin >> x;
a[i].first = x + min(i, n - i + 1);
a[i].second = x + i;
}
sort(a + 1, a + 1 + n);//对每个点的最小步排序
for (int i = 1; i <= n; ++i)sum[i] = sum[i - 1] + a[i].first;//记录最小步前缀和
for (int i = 1; i <= n; ++i)//遍历每个点作为第一步
{
int l = 0, r = n;
while (l <= r)//二分点数
{
int mid = (l + r) >> 1;
ll tmp = sum[mid] + a[i].second;
int cnt = mid + 1;//cnt记录总共可以走的点数(包括第一步)
if (i <= mid)tmp -= a[i].first, cnt--;//如果第一步的点在二分的前缀区间,减去a[i].first这个最小步,点数-1
if (tmp <= c)
{
ans = max(ans, cnt);
l = mid + 1;
}
else r = mid - 1;
}
}
cout << ans << endl;
}
return 0;
}