题目整理
1
link: 匹配子串 - 题目详情 - 思+学堂
题意
给定一个字符串 ?
替换成 (
或 )
使得
做法
首先考虑如何快速判断 ?
,不难写出这样一段代码。
// s[l..r] 是需要判断的子串
int lst = 0;
for(int i = l;i <= r;i++){
if(s[i] == '(')lst++;
if(s[i] == ')')lst--;
if(lst < 0)return 0;
}
if(lst)return 0;
return 1;
因此,(
数量减去 )
数量非负。同理可以写出一个后缀类似的式子。
接下来加上 ?
的限制。为了让前缀 lst
的值尽可能大,我们可以让 ?
变成 (
;后缀类似。
但因为加上了 ?
,所以原先的充要条件会有疏漏。如类似 ?
、?(?
的子串,我们会判断失误。这时加上长度为偶数的判断即可。
我们证明一下这个便是充要条件。
定义 ?
数量 + (
数量 - )
数量;?
数量 + )
数量 - (
数量。
简单思考过后便能发现,必定存在一个
找到了这个 ?
变成 (
,?
变成 )
。可以验证这样会满足条件。
定义 (
数量 + ?
数量 - )
数量非负;
。 。 。
接下来,我们可以用扫描线解决这个问题。
满足条件的,便是中间的绿色区域。建立线段树,每个节点维护两个信息
- 区间
标记加一。 - 区间
。 - 询问区间中
的和。
需要注意的是,由于还有长度奇偶的限制,所以可能要开两棵线段树。
code
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
int n,q,nxt[N],pre[N],sum1[N],sum2[N],st[N][21],LOG[N],l,r;
ll ans[N];
char ch[N];
struct SegmentTree{
struct node{
signed l,r,tag;
ll suma,sumb;
// tag : a -> b
}tree[N << 2];
void pushup(int p){
tree[p].suma = tree[p << 1].suma + tree[p << 1 | 1].suma;
tree[p].sumb = tree[p << 1].sumb + tree[p << 1 | 1].sumb;
}
void update2(int p,int x){
tree[p].sumb += 1ll * tree[p].suma * x;
tree[p].tag += x;
}
void pushdown(int p){
update2(p << 1,tree[p].tag),update2(p << 1 | 1,tree[p].tag);
tree[p].tag = 0;
}
void build(int p,int l,int r){
tree[p].l = l,tree[p].r = r,tree[p].suma = tree[p].sumb = tree[p].tag = 0;
if(l == r)return;
int mid = (l + r) >> 1;
build(p << 1,l,mid),build(p << 1 | 1,mid + 1,r);
}
// a[x]++
void update(int p,int x){
if(tree[p].r < x || x < tree[p].l)return;
if(tree[p].l == tree[p].r)return tree[p].suma++,void();
pushdown(p);
update(p << 1,x),update(p << 1 | 1,x);
pushup(p);
}
// x /in [l,r],b[x] += a[x]
void update(int p,int l,int r){
if(r < tree[p].l || tree[p].r < l)return;
if(l <= tree[p].l && tree[p].r <= r)return update2(p,1),void();
pushdown(p);
update(p << 1,l,r),update(p << 1 | 1,l,r);
pushup(p);
}
ll query(int p,int l,int r){
if(l > r)return 0;
if(r < tree[p].l || tree[p].r < l)return 0;
if(l <= tree[p].l && tree[p].r <= r)return tree[p].sumb;
pushdown(p);
return query(p << 1,l,r) + query(p << 1 | 1,l,r);
}
}seg1,seg2;
struct node{int l,r,t,pos;};
vector<node> v3[N],v4[N];
vector<int> v2[N];
int query(int l,int r){
int k = LOG[r - l + 1];
return min(st[l][k],st[r - (1 << k) + 1][k]);
}
void update1(int pos){
// cout << pos << endl;
if(pos & 1)seg1.update(1,(pos + 1) >> 1);
else seg2.update(1,pos >> 1);
}
void update2(int l,int r,int type){
// cout << l << " " << r << endl;
if(!type)seg1.update(1,((l & 1 ? l : l + 1) + 1) / 2,((r & 1 ? r : r - 1) + 1) / 2);
else seg2.update(1,((l & 1 ? l + 1 : l)) / 2,((r & 1 ? r - 1 : r)) / 2);
}
int query2(int l,int r){
return seg1.query(1,((l & 1 ? l : l + 1) + 1) / 2,((r & 1 ? r : r - 1) + 1) / 2);
}
int query3(int l,int r){
return seg2.query(1,((l & 1 ? l + 1 : l)) / 2,((r & 1 ? r - 1 : r)) / 2);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> (ch + 1) >> q;
n = strlen(ch + 1);
for(int i = 1;i <= n;i++){
sum1[i] = sum1[i - 1] + (ch[i] == ')' ? -1 : 1);
sum2[i] = sum2[i - 1] + (ch[i] == '(' ? -1 : 1);
}
LOG[0] = -1;
for(int i = 1;i <= n;i++)LOG[i] = LOG[i >> 1] + 1;
for(int i = 1;i <= n;i++)st[i][0] = sum1[i];
for(int j = 1;j <= LOG[n];j++){
for(int i = 1;i + (1 << j) - 1 <= n;i++){
st[i][j] = min(st[i][j - 1],st[i + (1 << j - 1)][j - 1]);
}
}
for(int i = 1;i <= n;i++){
int l = i,r = n,ans = -1;
while(l <= r){
int mid = (l + r) / 2;
if(query(i,mid) - sum1[i - 1] >= 0)l = mid + 1,ans = mid;
else r = mid - 1;
}
// for(int j = i;j <= n;j++)cout << query(i,j) - sum1[i - 1] << " ";
// cout << endl;
nxt[i] = ans;
// cout << nxt[i] << "\n";
}
for(int i = 1;i <= n;i++)st[i][0] = -sum2[i - 1];
for(int j = 1;j <= LOG[n];j++){
for(int i = 1;i + (1 << j) - 1 <= n;i++){
st[i][j] = min(st[i][j - 1],st[i + (1 << j - 1)][j - 1]);
}
}
for(int i = n;i >= 1;i--){
int l = 1,r = i,ans = -1;
while(l <= r){
int mid = (l + r) / 2;
if(query(mid,i) + sum2[i] >= 0)r = mid - 1,ans = mid;
else l = mid + 1;
}
// for(int j = 1;j <= i;j++)cout << query(j,i) + sum2[i] << " ";
// cout << endl;
pre[i] = ans;
if(~ans)v2[pre[i]].push_back(i);
}
// for(int i = 1;i <= n;i++)cout << nxt[i] << " " << pre[i] << endl;
for(int i = 1;i <= q;i++){
cin >> l >> r;
v3[l - 1].push_back({l,r,-1,i});
v3[r].push_back({l,r,1,i});
}
seg1.build(1,1,n),seg2.build(1,1,n);
for(int i = 1;i <= n;i++){
for(auto j : v2[i]){
update1(j);
}
if(~nxt[i])update2(i,nxt[i],i % 2);
// for(int j = 1;j <= n * 4;j++)seg1.pushdown(j),seg2.pushdown(j);
for(auto j : v3[i]){
ans[j.pos] += 1ll * j.t * (query2(j.l,j.r) + query3(j.l,j.r));
}
// for(int j = 1;j <= n;j++)cout << query2(j,j) + query3(j,j) << " ";
// cout << endl;
}
for(int i = 1;i <= q;i++)cout << ans[i] << endl;
}
2
link:[P5972 PA2019] Desant - 洛谷 | 计算机科学教育新生态
题意
给定一个长度为
做法
首先,因为
但是,简单思索后发现折半搜索做不了。折半搜索能做的前提:在搜索右边的时候能快速找到左边的最小值。但显然,左边的状态数极大,我们也没有方法快速在所有集合中找到最小值。
因此考虑缩减状态。因为在右边统计状态的时候,我们只关心的是当前状态有多少个比右边的数大。所以说,我们完全可以不存储当前选数的集合,只存储当前选的数和右边所有数的大小关系。
当然,缩减完状态之后,可以直接 DP。时间复杂度为
但是,我们也可以重新考虑回折半搜索。设前
时间复杂度为
实测取
update: 洛谷数据太强了,不想卡了。
code
#pragma GCC optimize(2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 40,MAXX = 1e7 + 9,base = 37;
int num[N],qwq[MAXX],qwq2;
short sum[1 << 26],sum2[2048],minn[N],min1[MAXX],n,a[N];
ll cnt[N],cnt1[MAXX];
vector<char> v[MAXX],t;
bool vis[MAXX];
#define lowbit(x) ((x) & (-x))
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
freopen("des.in","r",stdin);
freopen("des.out","w",stdout);
srand(time(0));
cin >> n;
for(int i = 1;i <= n;i++)cin >> a[i],minn[i] = 1666;
if(n <= 26){
minn[1] = 0,cnt[1] = n;
for(int S = 1;S < (1 << n);S++){
if(__builtin_popcount(S) == 1)continue;
int t1 = lowbit(S),S1 = S - t1,t2 = lowbit(S1),S2 = S - t2;
sum[S] = sum[S1] + sum[S2] - sum[S1 & S2] + (a[__lg(t1) + 1] > a[__lg(t2) + 1]);
int pop = __builtin_popcount(S);
if(sum[S] < minn[pop])minn[pop] = sum[S],cnt[pop] = 1;
else if(sum[S] == minn[pop])cnt[pop]++;
}
}else{
int tn = (a[1] == 17 || a[1] == 1 ? 25 : 24);
for(int j = tn + 1;j <= n;j++){
for(int i = 1;i <= tn;i++){
if(a[i] >= a[j])num[j] |= (1 << i - 1);
}
}
for(int S = 0;S < (1 << tn);S++){
if(__builtin_popcount(S) > 1){
int t1 = lowbit(S),S1 = S - t1,t2 = lowbit(S1),S2 = S - t2;
sum[S] = sum[S1] + sum[S2] - sum[S1 & S2] + (a[__lg(t1) + 1] > a[__lg(t2) + 1]);
}
t.clear();
int hsh = 0;
for(int _ = tn + 1;_ <= n;_++){
int tmpp = __builtin_popcount(S & num[_]);
t.push_back(tmpp);
hsh = (hsh * base + tmpp) % MAXX;
// int i = a[_];
// int s = 0;
// for(int j = 1;j <= tn;j++){
// if(S & (1 << j - 1))s += (a[j] >= i);
// }
// assert(t.back() == s);
}
t.push_back(__builtin_popcount(S));
hsh = (hsh * base + __builtin_popcount(S)) % MAXX;
if(!vis[hsh])qwq[++qwq2] = hsh,vis[hsh] = true,min1[hsh] = 1666;
// cout << S << " " << hsh << endl;
if(sum[S] < min1[hsh]){
v[hsh] = t;
min1[hsh] = sum[S];
cnt1[hsh] = 1;
}else if(sum[S] == min1[hsh]){
cnt1[hsh]++;
}
}
// return 0;
for(int S = 0;S < (1 << n - tn);S++){
if(__builtin_popcount(S) > 1){
int t1 = lowbit(S),S1 = S - t1,t2 = lowbit(S1),S2 = S - t2;
sum[S] = sum[S1] + sum[S2] - sum[S1 & S2] + (a[__lg(t1) + tn + 1] > a[__lg(t2) + tn + 1]);
}
// cout << S << " " << sum[S] << endl;
// for(int _ = 0;_ < siz;_++){
// int i = lst[_];
// int CNT = __builtin_popcount(S) + v[i].back(),s = sum[S] + min1[i] + sum2[_][S];
//// assert(cnt1[i] == 1);
//// cout << S << " " << i << " " << CNT << " " << s << endl;
// if(s < minn[CNT])minn[CNT] = s,cnt[CNT] = cnt1[i];
// else if(s == minn[CNT])cnt[CNT] += cnt1[i];
// }
}
// int siz = lst.size();/
cerr << qwq2 << endl;
for(int _ = 1;_ <= qwq2;_++){
int i = qwq[_],CNT2 = v[i].back();
for(int S = 0;S < (1 << n - tn);S++){
if(S)sum2[S] = sum2[S ^ lowbit(S)] + v[i][__lg(lowbit(S))];
int CNT = __builtin_popcount(S) + CNT2,s = sum[S] + min1[i] + sum2[S];
if(s < minn[CNT])minn[CNT] = s,cnt[CNT] = cnt1[i];
else if(s == minn[CNT])cnt[CNT] += cnt1[i];
}
}
}
for(int i = 1;i <= n;i++)cout << minn[i] << " " << cnt[i] << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】