D. Distinct Split
![](https://img-blog.csdnimg.cn/img_convert/f0a3e462dd94c4481d112c0f04626503.png)
![](https://img-blog.csdnimg.cn/img_convert/f43e15304ee2c8325b93703862ea47d0.png)
题意:给你一串字符,让你一分为二,问你分开后两串字符之间分别的不同字符数量之和是多少。
思路:我们可以先将所有的字符分给右边一串,然后从第一个字符开始慢慢分给左边,直到右边只剩一个停止,然后中间一直取最大值就好了。
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<map>
#include<list>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
using namespace std;
typedef pair<char, int>pll;
pll c[200005];
int h[150][150];
ll x[200005], y[200005], z[200005];
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
iosgo();
int t; cin >> t;
while (t--)
{
int g[27],l[27];
memset(g, 0, sizeof(g));
memset(l, 0, sizeof(l));
int a; cin >> a;
string k;
cin >> k; int cnt = 0;
for (int i = 0; i < a; i++)g[k[i] - 'a']++;
for (int i = 0; i < 26; i++)
{
if (g[i] > 0)cnt++;
}int ans = 0;
for (int i = 0; i < a-1; i++)
{
int q = k[i] - 'a';
l[q]++;
g[q]--;
if (l[q] == 1)cnt++;
if (!g[q])cnt--;
ans = max(ans, cnt);
}
cout << ans << endl;
}
return 0;
}
F. Range Update Point Query
![](https://img-blog.csdnimg.cn/img_convert/6e3586841a867cf2dbdca9019eb298f9.png)
![](https://img-blog.csdnimg.cn/img_convert/460a709ee6c11f1dd918326d8a7fe0df.png)
题意:给你一个大小为n的数组。数组中数据最大1e9,然后让你执行q次操作。操作分为两种,第一种:改变自L-R之间的所有数一次,变成这个数本身所有位置上的数字和,比如123改变一次->6。
第二种:输出数组x位置上的数。
思路:当明白题意之后,我们肯定明白不能够遍历,那用什么办法呢?由于数据最大是1e9,那么数字最大也就是99999999->72->9。最多变两次,那么变两次这个数就不要动了。我们可以存那些可以变动的数字的下标,进行简化操作,详情见代码:
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<map>
#include<list>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
using namespace std;
typedef pair<char, int>pll;
pll c[200005];
int h[150][150];
int x[200005], y[200005];
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
int giao(int f)
{
int summ = 0;
while (f)
{
int k;
k = f % 10;
summ += k;
f /= 10;
}
return summ;
}
signed main()
{
set<int>k;
iosgo();
int t;
cin >> t;
while (t--)
{
k.clear();
int n, q;
cin >> n >> q;
run(i, n)
{
cin >> x[i];
if (giao(x[i]) != x[i])
{
k.insert(i);//存储下标。
}
}
k.insert(n + 1);
run(i, q)
{
int a, b, c;
cin >> a;
if (a == 1)//分开操作
{
cin >> b >> c;
int f = b;
while (1)
{
auto it = k.lower_bound(f);//利用下标二分查找是否有能够改变的数据
int pos = *it;
if (pos > c)break;//如果下标大于C,那么代表没有可以更改的,跳过。
x[pos] = giao(x[pos]);//数据改变
if (x[pos] == giao(x[pos]))
{
k.erase(pos);//当数据最简时,把这个下标消除。
}
f= pos + 1;//切记不是f++,否则会出现有数据会被多次消除。
}
}
else
{
cin >> b;
cout << x[b] << endl;;
}
}
}
}
G2. Teleporters (Hard Version)
![](https://img-blog.csdnimg.cn/img_convert/5cb386259e7fd2af230eb07109022e6f.png)
![](https://img-blog.csdnimg.cn/img_convert/1521c9ae5e3701dcafa8bde7d5b3ceec.png)
题意:你一开始在位置0,现在有n个传送门,每一个传送门使用需要它位置上这么多的金币,且至多使用一次。每一次使用后你可以决定传送到位置0或者n+1。在移动过程中,你每移动距离1就会消耗1金币。题目给出你的金币量,问你至多可以使用多少个传送门。
思路:由于位置的不确定,每一个传送门对应的都有两个金币使用值,所以我们需要遍历,以每一个位置为初始使用传送门进行遍历演算,在这里可以使用前缀和寻找最大解。详情见注释。
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<map>
#include<list>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
#define FOR(l,n) for(int i=1;i<=n;i++)
using namespace std;
typedef pair<int, int>pll;
pll c[200005];
int h[150][150];
ll x[200005], y[200005],s[200005];
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
signed main()
{
iosgo();
int t; cin >> t;
while (t--)
{
memset(x, 0, sizeof(x));
memset(y, 0, sizeof(y));
memset(s, 0, sizeof(s));
int n, c;
cin >> n >> c;
run(i, n)cin >> x[i], y[i] = min(x[i] + i, x[i] + n - i + 1);//将y取最优值
sort(y + 1, y + n + 1);//取完排序
run(i, n)s[i] = s[i - 1] + y[i];//利用前缀和查询最优解。(由于传送位置的设定)
int ans = 0;
run(i, n)
{
if (x[i] + i > c)continue;//如果最初的传送门都用不了就跳过
int k = upper_bound(s + 1, s + n + 1, c - (x[i] + i)) - (s + 1);
//查询用完之后的对应前缀和的最大值。这里返回的位置是 - (s + 1),就是前缀和的下标。即答案
int yy = min(x[i] + i, x[i] + n - i + 1);
if (y[k] >= yy)//由于y是排好序的,如果你最多用到第k个传送门但是y[k]却>=yy,意思就是你使用 //的初始传送门是被算进了这个前缀和里面的,所以要加上这个值重找一边
{
k = upper_bound(s + 1, s + n + 1, c + yy - (x[i] + i)) - (s + 1);
ans = max(ans, k);
}
else
{
ans = max(ans, k+1);
}
}
cout << ans << endl;
}
}