This commit is contained in:
Zengtudor 2025-07-20 15:59:04 +08:00
parent 5037f4d54e
commit 393e17bd1f
4 changed files with 217 additions and 2 deletions

53
src/7/20/T371062s1.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <algorithm>
#include <cstdio>
#include <deque>
#include <iostream>
#include <limits>
#include <string>
using ll = long long;
ll n,p,q,x,y,ans=std::numeric_limits<ll>::max();
std::string s;
int main(){
scanf("%lld%lld%lld%lld%lld",&n,&p,&q,&x,&y);
std::cin>>s;
for(ll msk=0;msk<(1ll<<n);msk++){
std::string ns = s;
ll nans{};
for(ll i=0;i<ns.size();i++){
if((1ll<<i)&msk){
if(ns[i]=='+'){
ns[i]='-';
}else{
ns[i]='+';
}
nans+=x;
}
}
std::deque dq(ns.begin(),ns.end());
for(ll i=0;i<n;i++){
ll now{p};
for(auto it = dq.begin();it!=dq.end();it++){
if((*it)=='+'){
now++;
}else{
if(now==0){
goto next;
}
now--;
}
}
if(now==q){
ans=std::min(ans,nans);
}
next:;
auto eit = --dq.end();
char ec = *eit;
dq.erase(eit);
dq.push_front(ec);
nans+=y;
}
}
printf("%lld\n",ans);
}

160
src/7/20/T635780.md Normal file
View File

@ -0,0 +1,160 @@
为了解决这个问题我们需要找到给定数组中所有连续子段和中的前k小个数的和。由于子段的数量是O(n²)的直接枚举所有子段并排序在n较大时如10^6是不可行的。因此我们采用二分答案结合树状数组和离散化的高效方法。
### 方法思路
1. **前缀和计算**:首先计算数组的前缀和数组`pre`,其中`pre[i]`表示从`a[1]`到`a[i]`的和。
2. **离散化**:将前缀和数组`pre`中的值进行离散化处理,以便在树状数组中高效查询。
3. **二分答案**通过二分查找确定第k小的子段和`X0`。具体地,我们使用`count(X)`函数统计子段和不超过X的个数通过二分调整X的值直到找到满足条件的最小X0。
4. **计算前k小子段和的和**:利用`count_sum(X0-1)`计算所有小于X0的子段和的总和然后加上剩余的k - cnt_less个X0的值得到最终结果。
### 解决代码
```cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000010;
int n;
ll k;
ll a[maxn];
ll pre[maxn];
vector<ll> d;
int m;
int id[maxn];
struct Fenw {
vector<ll> c_cnt, c_sum;
int size;
void init(int n) {
size = n;
c_cnt.assign(n+1, 0);
c_sum.assign(n+1, 0);
}
void add_cnt(int i, ll v) {
for (; i <= size; i += i & -i)
c_cnt[i] += v;
}
void add_sum(int i, ll v) {
for (; i <= size; i += i & -i)
c_sum[i] += v;
}
ll query_cnt(int i) {
if (i <= 0) return 0;
ll res = 0;
for (; i; i -= i & -i)
res += c_cnt[i];
return res;
}
ll query_sum(int i) {
if (i <= 0) return 0;
ll res = 0;
for (; i; i -= i & -i)
res += c_sum[i];
return res;
}
} fenw;
void discrete() {
d.clear();
for (int i = 0; i <= n; i++)
d.push_back(pre[i]);
sort(d.begin(), d.end());
d.erase(unique(d.begin(), d.end()), d.end());
m = d.size();
for (int i = 0; i <= n; i++) {
id[i] = lower_bound(d.begin(), d.end(), pre[i]) - d.begin() + 1;
}
}
ll count_func(ll X) {
fenw.init(m);
for (int i = 1; i <= n; i++) {
fenw.add_cnt(id[i], 1);
}
int j = 0;
ll cnt_total = 0;
for (int l = 1; l <= n; l++) {
ll key = pre[l-1] + X;
while (j < m && d[j] <= key)
j++;
if (j > 0) {
ll cnt1 = fenw.query_cnt(j);
cnt_total += cnt1;
}
fenw.add_cnt(id[l], -1);
}
return cnt_total;
}
pair<ll, ll> count_sum_func(ll X) {
fenw.init(m);
for (int i = 1; i <= n; i++) {
fenw.add_cnt(id[i], 1);
fenw.add_sum(id[i], pre[i]);
}
int j = 0;
ll cnt_total = 0;
ll sum_total = 0;
for (int l = 1; l <= n; l++) {
ll key = pre[l-1] + X;
while (j < m && d[j] <= key)
j++;
if (j > 0) {
ll cnt1 = fenw.query_cnt(j);
ll sum1 = fenw.query_sum(j);
cnt_total += cnt1;
sum_total += (sum1 - pre[l-1] * cnt1);
}
fenw.add_cnt(id[l], -1);
fenw.add_sum(id[l], -pre[l]);
}
return {cnt_total, sum_total};
}
int main() {
scanf("%d%lld", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
pre[0] = 0;
for (int i = 1; i <= n; i++) {
pre[i] = pre[i-1] + a[i];
}
discrete();
ll L = 0, R = pre[n];
while (L < R) {
ll mid = (L + R) >> 1;
if (count_func(mid) >= k) {
R = mid;
} else {
L = mid + 1;
}
}
ll X0 = L;
pair<ll, ll> res;
if (X0 - 1 < 0) {
res = {0, 0};
} else {
res = count_sum_func(X0 - 1);
}
ll cnt_less = res.first;
ll sum_less = res.second;
ll cnt_equal = k - cnt_less;
ll ans = sum_less + cnt_equal * X0;
printf("%lld\n", ans);
return 0;
}
```
### 代码解释
1. **前缀和计算**:首先计算数组`a`的前缀和数组`pre`,用于快速计算任意子段和。
2. **离散化**:将前缀和数组`pre`中的值排序并去重,生成离散化数组`d`,并建立映射`id`,将原前缀和值映射到离散化后的下标。
3. **二分查找第k小的子段和**
- 使用`count_func(X)`函数统计子段和不超过`X`的个数。该函数利用树状数组高效维护当前右端点前缀和,并通过移动指针`j`避免重复二分查找。
- 通过二分调整`X`的值,直到找到满足条件的最小`X0`,使得子段和不超过`X0`的个数至少为`k`。
4. **计算前k小子段和的和**
- 使用`count_sum_func(X0-1)`计算所有小于`X0`的子段和的总和`sum_less`及个数`cnt_less`。
- 剩余的子段和均为`X0`,个数为`k - cnt_less`。
- 最终结果为`sum_less + (k - cnt_less) * X0`。
该方法高效地利用了二分答案和树状数组,结合离散化处理,确保在较大数据规模下仍能在合理时间内求解。

3
src/7/20/T635780fix.cpp Normal file
View File

@ -0,0 +1,3 @@
int main(){
}

View File

@ -1,4 +1,3 @@
#include <cstdio>
int main(){
printf("[%10.c]\n",'^');
}