2026 暑假 C 班模拟 Day1 之全员 AK | 正睿 OI
合并果子
一眼二分,check 直接瞎搞一通。
一开始地瞎搞了 1h 的链表,发现自己是糖丸。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
int c,T,n,k;
int a[N];
bool check(int x){
int j=0,now=a[1];
for(int i=2;i<=n;i++){
if(now<x) now+=a[i],j++;
else now=a[i];
}
if(now<x&&j+1>k) return 0;
return (j<=k);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>c>>T;
while(T--){
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
int l=1,r=1e15,ans;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
cout<<ans<<"\n";
}
return 0;
}旮旯给木
糖糖题,大样例把正解喂给你了,证明也很简单。
#include<bits/stdc++.h>
using namespace std;
int T,x;
bool solve(int x){
while(!(x&1)) x>>=1;
if(x==1) return 1;
return 0;
}
signed main(){
cin>>T;
while(T--){
cin>>x;
cout<<(solve(x)?"Win\n":"Lose\n");
}
return 0;
}晚安。
注意到 h <= 20,把每个数拆成高 10 位和低 10 位。
$cost(p,q)$ 可以拆成只与高位相关的部分和只与低位相关的部分之和:
$ cost(p,q) = high(p高, q高) + low(p低, q低)$
预处理出所有高-高贡献 $hc[x][y]$ 和低-低贡献 $lc[x][y]$,其中 $x,y ∈ [0, 2^10)$。
对于固定的一个数 p,我们想快速求出 $max_q cost(p,q)$。
设 $p$ 的高位为 $ph$,低位为 $pl$。则 $cost(p,q) = hc[ph][qh] + lc[pl][ql]$。
令 $f[ph][ql]$ 表示已存在的数中,当另一个数的高位任意、低位为 $ql$ 时,$lc[pl][ql] $ 的最大值;
$g[pl][qh]$ 表示已存在的数中,当另一个数的低位任意、高位为 $qh$ 时,$hc[ph][qh]$ 的最大值。
那么 $max cost = max_{ql}$ ( $f[ph][ql] + max_{qh} hc[ph][qh]$ 与 $g$ 相关的组合 )
实际上通过维护 $f$ 和 $g$,插入一个数时只需枚举另一维的所有可能值(共 $2^10$ 种)更新答案。
为了快速获得 $f$ 和 $g$ 的初始值(对于存在的数),可以用 DP 逐位处理:
对于每个固定的高位 $id$,初始知道哪些低位存在,DP 后得到对于任意低位 $x$,当配对数的低位为 $x$ 时的最大低位贡献。
整体复杂度为 O(2^{h/2} * h + (n+q) * 2^{h/2}),可以通过。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int V=1<<10;
int a[30],b[30],c[30];
int p[V][V],q[V][V],vh[V][V],vl[V][V];
int f[2][V],g[2][V];
void solve(){
int n,h,Q;
cin>>n>>h>>Q;
for(int i=0;i<h;i++) cin>>a[i];
for(int i=0;i<h;i++) cin>>b[i];
for(int i=0;i<h;i++) cin>>c[i];
for(int i=1;i<=n;i++){
int x;
cin>>x;
int hx=x>>10,lx=x&(V-1);
p[hx][lx]=1,q[lx][hx]=1;
}
for(int id=0;id<V;id++){
for(int i=0;i<V;i++){
if(p[id][i]) f[1][i]=0; else f[1][i]=-1e9;
if(q[id][i]) g[1][i]=0; else g[1][i]=-1e9;
}
for(int i=0;i<10;i++){
int cur=i&1,pre=cur^1;
for(int j=0;j<V;j++) f[cur][j]=-1e9,g[cur][j]=-1e9;
for(int j=0;j<V;j++){
int bit=(j>>i)&1,nxt=j^(1<<i),u=i+10;
if(bit==0){
if(f[pre][j]>f[cur][j]) f[cur][j]=f[pre][j];
if(f[pre][j]+a[i]+c[i]>f[cur][nxt]) f[cur][nxt]=f[pre][j]+a[i]+c[i];
if(g[pre][j]>g[cur][j]) g[cur][j]=g[pre][j];
if(g[pre][j]+a[u]+c[u]>g[cur][nxt]) g[cur][nxt]=g[pre][j]+a[u]+c[u];
}else{
if(f[pre][j]+a[i]+c[i]>f[cur][nxt]) f[cur][nxt]=f[pre][j]+a[i]+c[i];
if(f[pre][j]+a[i]+b[i]>f[cur][j]) f[cur][j]=f[pre][j]+a[i]+b[i];
if(g[pre][j]+a[u]+c[u]>g[cur][nxt]) g[cur][nxt]=g[pre][j]+a[u]+c[u];
if(g[pre][j]+a[u]+b[u]>g[cur][j]) g[cur][j]=g[pre][j]+a[u]+b[u];
}
}
}
for(int i=0;i<V;i++) p[id][i]=f[1][i],q[id][i]=g[1][i];
}
int ans=-1e9;
for(int i=0;i<V;i++) for(int j=0;j<V;j++){
int s=0,t=0;
for(int k=0;k<10;k++){
int bp=(i>>k)&1,bq=(j>>k)&1,u=k+10;
s+=(bp|bq)*a[u]+(bp&bq)*b[u]+(bp^bq)*c[u];
t+=(bp|bq)*a[k]+(bp&bq)*b[k]+(bp^bq)*c[k];
}
vh[i][j]=s,vl[i][j]=t;
if(p[i][j]+q[j][i]>ans) ans=p[i][j]+q[j][i];
}
cout<<ans<<' ';
while(Q--){
int x;
cin>>x;
int hx=x>>10,lx=x&(V-1);
for(int i=0;i<V;i++){
if(vl[lx][i]>p[hx][i]) p[hx][i]=vl[lx][i];
if(vh[hx][i]>q[lx][i]) q[lx][i]=vh[hx][i];
if(p[hx][i]+q[i][hx]>ans) ans=p[hx][i]+q[i][hx];
if(p[i][lx]+q[lx][i]>ans) ans=p[i][lx]+q[lx][i];
}
cout<<ans<<' ';
}
cout<<'\n';
for(int i=0;i<V;i++) for(int j=0;j<V;j++) p[i][j]=q[i][j]=vh[i][j]=vl[i][j]=0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int c,T;
cin>>c>>T;
while(T--) solve();
return 0;
}括号序列
题解
将 ( 看作 $+1$,) 看作 $-1$。记原串的前缀和为:
$$
a[i] = \sum_{k=1}^{i} v_k \quad (v_k = \pm 1),\qquad a[0] = 0
$$
原串是合法括号序列,因此对所有 $i$ 有 $a[i] \ge 0$,且 $a[n] = 0$。
翻转区间 $[l, r]$ 后,对于位置 $i$:
- 若 $i < l$ 或 $i > r$,字符不变,新前缀和仍为 $a[i]$。
- 若 $l \le i \le r$,位置 $i$ 的字符变成了原位置 $l + r - i$ 的字符。因此新前缀和为:
$$
a'[i] = a[l-1] + \big( a[r] - a[l+r-i-1] \big)
$$
翻转后串仍是合法括号序列的充要条件是所有 $a'[i] \ge 0$。对于 $i \in [l, r]$,要求:
$$
a[l-1] + a[r] - a[l+r-i-1] \ge 0
$$
当 $i$ 遍历 $[l, r]$ 时,$l+r-i-1$ 恰好遍历 $[l-1, r-1]$。因此上式等价于要求 $a[l-1] + a[r]$ 不小于这段区间中 $a$ 的最大值:
$$
a[l-1] + a[r] \ge \max_{j = l-1}^{r-1} a[j]
$$
由于 $a[r]$ 本身就出现在该区间内且 $a[r] \ge 0$,把上界扩展到 $r$ 不改变条件,可得更对称的形式:
$$
a[l-1] + a[r] \ge \max_{j = l-1}^{r} a[j]
$$
令 $L = l-1$,$R = r$,则原问题转化为:
统计所有 $0 \le L < R \le n$,满足 $\displaystyle a[L] + a[R] \ge \max_{i=L}^{R} a[i]$。
只统计跨过中点 $mid$ 的区间对 $(L, R)$(即 $L \le mid < R$),然后在左右半边递归。这样每对 $(L, R)$ 会在 $mid$ 为其所在区间分裂点时被统计恰好一次。
对于跨中点的区间,把最大值拆成左右两部分:
$$
b[L] = \max_{i=L}^{mid} a[i], \qquad b[R] = \max_{i=mid+1}^{R} a[i]
$$
显然 $\max_{i=L}^{R} a[i] = \max(b[L], b[R])$,条件变为:
$$
a[L] + a[R] \ge \max(b[L], b[R])
$$
关键观察:$b$ 数组天然具有单调性。当 $L$ 从 $mid$ 向左移动时,覆盖范围扩大,$b[L]$ 只增不减;同理 $R$ 向右移动时 $b[R]$ 只增不减。
将左侧所有点 $(a[L], b[L])$ 和右侧所有点 $(a[R], b[R])$ 放在一起,按 $b$ 值从小到大处理。
处理过程中维护两个集合:已加入的左侧点、已加入的右侧点。当一个点被处理时,另一侧中所有已加入点的 $b$ 值都不超过当前点的 $b$ 值,因此当前点决定了这一批配对中的最大值。
- 当前处理左侧点 $L$:另一侧已加入的 $R$ 满足 $b[R] \le b[L]$,故 $\max = b[L]$。条件化简为: $$
a[R] \ge b[L] - a[L]
$$ 查询右侧已加入点中 $a$ 值 $\ge$ 该阈值的个数即可。 - 当前处理右侧点 $R$:对称地,条件为: $$
a[L] \ge b[R] - a[R]
$$ 查询左侧已加入点中 $a$ 值 $\ge$ 该阈值的个数。
处理完当前点后,将其加入对应侧的集合。
每对 $(L, R)$ 会在 $b$ 值较大的那一方被处理时被统计恰好一次,不重不漏。
需要支持的操作:
- 向集合中加入一个 $a$ 值;
- 查询集合中 $a$ 值 $\ge x$ 的元素个数。
$a$ 的值域在 $[0, n]$ 范围内,用树状数组即可在 $\mathcal{O}(\log n)$ 时间内完成每次操作。由于归并过程中每个元素各引起一次加入和若干次查询,单层分治的复杂度为 $\mathcal{O}(n \log n)$。
分治共 $\log n$ 层,总时间复杂度 $\mathcal{O}(n \log^2 n)$,常数很小。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e6+5;
int n,mx,cur;
string s;
int a[N];
struct bit{
int tr[N],ver[N],t;
void init(){t=++cur;}
void add(int x,int k){
x++;
int sz=mx+5;
while(x<=sz){
if(ver[x]!=t) ver[x]=t,tr[x]=0;
tr[x]+=k;
x+=x&-x;
}
}
int sum(int x){
if(x<0) return 0;
x++;
int res=0;
while(x>0){
if(ver[x]==t) res+=tr[x];
x-=x&-x;
}
return res;
}
}bl,br;
pair<int,int> vl[N],vr[N];
int calc(int l,int mid,int r){
int mxv=a[mid],sl=0,sr=0;
for(int i=mid;i>=l;i--) mxv=max(mxv,a[i]),vl[++sl]={a[i],mxv};
mxv=a[mid+1];
for(int i=mid+1;i<=r;i++) mxv=max(mxv,a[i]),vr[++sr]={a[i],mxv};
int i=1,j=1,ans=0;
bl.init(),br.init();
while(i<=sl||j<=sr){
if(j>sr||(i<=sl&&vl[i].second<=vr[j].second)){
int al=vl[i].first,blv=vl[i].second,nd=blv-al;
if(nd<=mx) ans+=br.sum(mx)-(nd>0?br.sum(nd-1):0);
bl.add(al,1),i++;
}
else{
int ar=vr[j].first,brv=vr[j].second,nd=brv-ar;
if(nd<=mx) ans+=bl.sum(mx)-(nd>0?bl.sum(nd-1):0);
br.add(ar,1),j++;
}
}
return ans;
}
int solve(int l,int r){
if(l>=r) return 0;
int mid=(l+r)>>1;
int ans=solve(l,mid)+solve(mid+1,r);
ans+=calc(l,mid,r);
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>s;
n=s.size();
for(int i=0;i<n;i++) a[i+1]=a[i]+(s[i]=='('?1:-1);
for(int i=0;i<=n;i++) mx=max(mx,a[i]);
cout<<solve(0,n)<<"\n";
return 0;
}


Comments 1 条评论