C-小苯的字符串变化
思路:
- 前缀和
- 创建两个数组存储前缀和
- 一个数组为
pre_up[i]
代表从0
到i
的大写字符的数量 - 一个数组为
pre_low[i]
代表从0
到i
的小写字符的数量
- 从头到尾,遍历查找小值即可
以下是代码部分
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 1e5 + 10;
string s;
int pre_up[N];
int pre_low[N];
void solve()
{
cin >> s;
int n = (int)s.size();
for(int i = 0; i < n; i ++)
{
if(islower(s[i])) pre_low[i] = 1;
else pre_up[i] = 1;
}
//前缀和
for(int i = 0; i < n; i ++)
{
pre_low[i] += pre_low[i - 1];
pre_up[i] += pre_up[i - 1];
}
//遍历查找
int ans = 1e9;
for(int i = 0; i < n - 1; i ++)
ans = min(ans, pre_low[i] + pre_up[n - 1] - pre_up[i]);
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
//cin >> t;
while(t --)
solve();
return 0;
}
D-小红的子数组排列判断
思路:
-
本题主要用到了
map<int, int>
- 定义一个
map<>
,用来储存长度为k
的区间的各个数的数量 - 滑动长度为
k
的窗口区段,根据情况,判断是否为排列
- 定义一个
-
本题排列的判断方法
judge
代表窗口中符合条件的数,如果judge == k
则为排列,否则不是。- 首先把数字存入
map
中 - 如果
a[i] >= 1 && a[i] <= k && mp[a[i]] == 1
条件符合,则judge++
以下是代码部分
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 1e5 + 10;
int a[N];
map<int, int> mp;
void solve()
{
int n, k;
//ans记录总共符合排列的数量
ll ans = 0;
cin >> n >> k;
for(int i = 1; i <= n; i ++)
cin >> a[i];
int judge = 0;
//类似于哈希,存于map中,并记录合法的数的数量
//注意窗口长度为k
for(int i = 1; i <= k; i ++)
{
mp[a[i]] ++;
if(a[i] >= 1 && a[i] <= k && mp[a[i]] == 1)
judge ++;
}
//如果是一个排列,则总数 = 1
if(judge == k) ans = 1;
//窗口的滑动遍历
for(int i = k + 1; i <= n; i ++)
{
//先存入map中
mp[a[i]] ++;
//判断是否合法,再判断是否从原先的0变为1,则要 + 1
if(a[i] >= 1 && a[i] <= k && mp[a[i]] == 1)
judge ++;
//窗口在滑动后,头部元素的删除,如果原先为合法数据,且为1
//则删去后,合法数量-1
if(a[i - k] >= 1 && a[i - k] <= k && mp[a[i - k]] == 1)
judge --;
//map中的元素删除
mp[a[i - k]] --;
//如果合法数为k,则符合排列,ans + 1
if(judge == k) ans ++;
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
//cin >> t;
while(t --)
solve();
return 0;
}
E - 小红的平行四边形
思路:
-
利用
vector<pair<ll, ll>> p
存储点集 -
利用
map<pair<ll, ll>, vector<pair<ll, ll> > > mp
,key
存储向量,value
存储向量起点的点集 -
在一个平面上,如果存在两个向量相同(且不重合),则一定可以组成平行四边形。
-
组成平行四边形的面积大小计算
-
两条组成平行四边形的线截距差值(向量相同的情况下)来进行比较(差值越大,面积越大)
-
平行四边形的面积计算可用向量的外积计算
- 如果 向量a 与 向量b 相交 则 a × × × b = = = ∣ | ∣ a ∣ ∗ ∣ | * | ∣∗∣ b ∣ | ∣ * sin \sin sin < a , b > <a, b> <a,b>。
-
以下是代码部分,代码参考来源——Heltion
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 1e3 + 10;
//点集
vector<pair<ll, ll>> p;
//向量集
map<pair<ll, ll>, vector<pair<ll, ll> > > mp;
ll temp_x, temp_y;
//根据截距的大小进行从小到大排序
bool cmp(pair<ll, ll> p1, pair<ll, ll> p2)
{
auto [x1, y1] = p1;
auto [x2, y2] = p2;
return x1 * temp_y - y1 * temp_x < x2 * temp_y - y2 * temp_x;
}
void solve()
{
int n;
cin >> n;
//给数组p[]赋初值
p.assign(n, {});
//输入
for(auto& [x, y] : p) cin >> x >> y;
sort(p.begin(), p.end());
//把点集的组合存入map中
for(int i = 0; i < n; i ++)
for(int j = 0; j < n; j ++)
if(i != j)
{
//p[i]的first赋值给xi, second赋值给yi
auto[xi, yi] = p[i];
auto[xj, yj] = p[j];
// mp[向量] = {向量的初始位置}
//存储的是向量相同,初始位置不同
mp[{xj - xi, yj - yi}].emplace_back(xi, yi);
}
//记录最大面积
ll ans = 0;
//遍历map
for(auto [pai_d, v] : mp)
{
//拿出向量的(x, y)
temp_x = pai_d.first;
temp_y = pai_d.second;
//根据截距的大小进行从小到大排序
sort(v.begin(), v.end(), cmp);
//如果v的数量 >= 2, 就可以组成平行四边形
if(v.size() != 1)
{
//找到截距最大值和最小值分别对应的向量起点坐标
auto [x1, y1] = v[0];
auto [x2, y2] = v.back();
//求向量的外积,模长刚好为平行四边形的面积
ll ps = (x2 * temp_y - y2 * temp_x) - (x1 * temp_y - y1 * temp_x);
//取最大值
ans = max(ans, ps);
}
}
if(ans) cout << ans << ".0";
else cout << "-1";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while(t --)
solve();
return 0;
}
F-小红小紫画线
思路:
- 选四个点,作为两条线,因为在凸多边形上,所以四个点一定不共线。
- 则一定有一种连接方式使得两条直线相交。
- 因为两条直线可以交换,所以选了四个点后,一次有两种方案。
- 首先,求出所有C(sum, 4) 和 C(sum, 3),选四个点的组合数,选三个点的组合数之和。
- 去除不合法的方案:
- 1.C(sum, 4)时,四个点在一条边上,C(sum, 3)时三个点在一条边上
- 2.C(sum, 4)时,三个点在一条边上,C(sum, 3)时二个点在一条边上
3.去除之后,就是合法方案数
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int mod = 1e9 + 7;
vector<ll> a;
//C(n, m) = n! / (n - m)! / (m)!
//求组合数
ll C(ll n, int m)
{
ll p = 1;
for(int i = 1; i <= m; i ++)
{
p = p * (n - i + 1) % mod;
//防止有余数,除不尽。规避乘法逆元
while(p % i) p += mod;
p /= i;
}
return p;
}
void solve()
{
int n;
cin >> n;
a.assign(n + 1, {});
ll sum = 0;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
sum += a[i];
}
sum %= mod;
//所有由四个点构成的两条线的组合, 所有由三个点构成的两条线的组合 相加
ll ans = (C(sum, 4) * 2 + C(sum, 3) * 6) % mod;
for(int i = 1; i <= n; i ++)
{
// 选4个点的情况下,三个点在一条边上是不允许的。选三个点的情况下,两个点在一条边上是不允许的。
ll p = (C(a[i], 3) + C(a[i], 2) * 2) * 2 % mod * (sum + mod - a[i]) % mod;
ans = (ans + mod - p) % mod;
//选的点都在一条边上的方案数
ll q = (C(a[i], 4) * 2 + (C(a[i], 3)) * 6) % mod;
ans = (ans + mod - q) % mod;
}
cout << ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while(t --)
solve();
return 0;
}```