当前位置: 首页 > news >正文

sm2025 模拟赛19 (2025.10.14)

文章目录

  • T1 深度优先搜索
  • T2 弹珠
  • T3 字符串
  • T4 酒厂

T1 深度优先搜索

link
思路
考虑叶子结点向上合并,注意到如果相邻两个数差为 1 1 1 那么这两个数很可能是同一个父亲的两个儿子。于是题目相当于有一堆数,每次可以合并相邻两个差为 1 1 1 的数(相当于删掉较大的数),最后只能剩一个数且这个数为 0
考虑单调栈维护,但是我们不可以能合并就合并,因为这样如果将前面可以合并的数合并完了之后剩下一个很小的数但后面来了一个很大的数时就会被判不合法,实际上他可能时合法的。所以说在单调栈中维护一个以 a i a_i ai 为结尾的最长不降序列,这样既能保证数不会太小又可以保证 a i + 1 a_{i+1} ai+1 有合并的机会。具体地,加入一个数 a i a_i ai 就将此时栈顶的 < a i \lt a_i <ai 的数 pop 掉。复杂度 O ( n ) O(n) O(n)
代码

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn],q[maxn];
int main(){freopen("dfs.in","r",stdin);freopen("dfs.out","w",stdout);int t; scanf("%d",&t);while(t--){int n; scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);int top=0;for(int i=1;i<=n;i++){while(top&&a[q[top]]>=a[i]){if(top>1&&a[q[top]]-a[q[top-1]]==1) top--;else break;}while(top&&a[i]<a[q[top]]){if(a[q[top]]-a[i]==1) top--;else break;}q[++top]=i;}bool ok=0;for(int i=top;i>1;i--)if(a[q[i]]-a[q[i-1]]!=1){ok=1;break;}(ok||a[q[1]])?printf("NO\n"):printf("YES\n");}return 0;
}

T2 弹珠

link
思路
相当于从每个起点出发除起点外每遇到一个与当前方向不同的格子(对于起点的左边是 > ,起点的右边是 <)则会折返回来往另一边走。相当于起点 左边> / 右边< 小的那一边会被走完,另一边则会走相应的方向不同的格子数,于是可以预处理出前左边的 > 数量和右边的 < 数量以及对于每个 >/< 离起点的距离,可以前缀和优化,分离计算左右两边答案的时候 × 2 \times 2 ×2 就好(因为会往返来回走) 。复杂度 O ( n l o g n ) O(nlogn) O(nlogn) ,可以优化到 O ( n ) O(n) O(n) 。多加注意实现的细节和简洁!

反思
没有想到起点左右两边任意一边会走完,即可以确定折返的次数,于是荣获 O ( n 2 ) O(n^2) O(n2) 。但是一直在想起点从 i i i 移到 i + 1 i+1 i+1 对答案有什么贡献的区别可以快速算,事实证明行不通。所以要多角度思考。

代码

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;
int a[maxn],b[maxn];
ll suma[maxn],sumb[maxn];
string st;
int main(){freopen("pinball.in","r",stdin);freopen("pinball.out","w",stdout);int t; cin>>t;while(t--){int n; cin>>n>>st; st=" "+st; int na=0,nb=0;for(int i=1;i<=n;i++){if(st[i]=='<') a[++na]=i;else b[++nb]=i;}for(int i=1;i<=na;i++)suma[i]=suma[i-1]+a[i];for(int i=1;i<=nb;i++)sumb[i]=sumb[i-1]+b[i];for(int i=1;i<=n;i++){int x=lower_bound(b+1,b+nb+1,i)-b-1,y=na-(upper_bound(a+1,a+na+1,i)-a)+1;if(st[i]=='<'){if(x<=y) cout<<(suma[na-y+x]-suma[na-y]-sumb[x])*2+i<<" ";else cout<<(suma[na]-suma[na-y]-(sumb[x]-sumb[x-y-1]))*2+i*2+n+1-i<<" ";}else{if(y<=x) cout<<(suma[na]-suma[na-y]-(sumb[x]-sumb[x-y]))*2+n+1-i<<" ";else cout<<(suma[na-y+x+1]-suma[na-y]-sumb[x])*2-i<<" ";}}cout<<"\n";}return 0;
}

T3 字符串

link
思路
假设 a i ≤ b i a_i \le b_i aibi ,可以发现删掉 a i , b i a_i,b_i ai,bi 之后对于字符串 a i a_i ai 前面的位置是相同的, b i b_i bi 后面的位置也是相同的,即只用比较 ( a i , b i ] (a_i,b_i] (ai,bi] [ a i , b i ) [a_i,b_i) [ai,bi) 这两段字符就好。
考虑限制 ( a i , b i ) (a_i,b_i) (ai,bi)

  • a i < b i a_i \lt b_i ai<bi ,相当于 S [ a i , b i ] S[a_i,b_i] S[ai,bi] 中第一个 S x ≠ S x + 1 S_x \ne S_{x+1} Sx=Sx+1 的位置 x x x 满足 S x > S x + 1 S_x \gt S_{x+1} Sx>Sx+1 x x x 不存在,记为 第一类约束;
  • a i > b i a_i \gt b_i ai>bi ,相当于 S [ b i , a i ] S[b_i,a_i] S[bi,ai] 中第一个 S x ≠ S x + 1 S_x \ne S_{x+1} Sx=Sx+1 的位置 x x x 满足 S x < S x + 1 S_x \lt S_{x+1} Sx<Sx+1 x x x 不存在,记为第二类约束。

f i , j f_{i,j} fi,j 表示考虑完前 i i i 个字符, S i = j S_i=j Si=j 的方案数。考虑转移:

  • S i − 1 = S i S_{i-1}=S_i Si1=Si ,没有限制, f i , j ← f i − 1 , j f_{i,j} \leftarrow f_{i-1,j} fi,jfi1,j
  • S i − 1 > S i S_{i-1} \gt S_i Si1>Si ,枚举上一个 S k − 1 ≠ S k S_{k-1} \ne S_k Sk1=Sk 的位置 k k k ,要满足所有 l ≥ k , r ≥ i l \ge k,r \ge i lk,ri 的限制且不存在第二类限制,于是有 k ≤ l < i ≤ r k\le l \lt i \le r kl<ir f i , j ← f k , x − f k − 1 , x f_{i,j} \leftarrow f_{k,x}-f_{k-1,x} fi,jfk,xfk1,x ( 因为 S k ≠ S k − 1 S_{k} \ne S_{k-1} Sk=Sk1),其中 x > j x\gt j x>j 。可以前缀和维护 x x x ,优化到 O ( 26 n 2 ) O(26n^2) O(26n2) ,仍超时。发现合法的 k k k 为一段后缀,找到第一个合法的 k ′ k' k ,(注意:此时 k k k 最小取到 k ′ + 1 k'+1 k+1 ,因为 S k ′ + 1 ≠ S k ′ S_{k'+1} \ne S_{k'} Sk+1=Sk ,而 S k ′ ≠ S k ′ − 1 S_{k'}\ne S_{k'-1} Sk=Sk1 是没必要的), f i , j ← ( ∑ k = k ′ + 1 i − 1 f k , x − f k − 1 , x ) = f i − 1 , x − f k ′ , x f_{i,j} \leftarrow (\sum\limits_{k=k'+1}^{i-1}f_{k,x}-f_{k-1,x})=f_{i-1,x}-f_{k',x} fi,j(k=k+1i1fk,xfk1,x)=fi1,xfk,x ,这样复杂度为 O ( 26 n ) O(26n) O(26n)
  • S i − 1 < S i S_{i-1} \lt S_i Si1<Si 同理。

k ′ k' k 可以通过 set 维护合法的限制区间。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e5+5,mod=1e9+7;
ll f[maxn][30];
vector<int>ql[maxn],qr[maxn],ql2[maxn],qr2[maxn];
multiset<int>s,s2;
int main(){int n,m; cin>>n>>m;for(int i=1;i<=m;i++){int x,y; cin>>x>>y;if(x<y) ql[x].push_back(y),qr[y].push_back(x);else ql2[y].push_back(x),qr2[x].push_back(y);}for(int i=0;i<26;i++)f[1][i]=1;for(auto j:ql[1]) s.insert(1);for(auto j:ql2[1]) s2.insert(1);for(int i=2;i<=n;i++){for(int j=0;j<26;j++)(f[i][j]+=f[i-1][j])%=mod;int k=(s2.empty()?0:*s2.rbegin()); ll sum=0;for(int j=25;j>=0;j--)// s[i-1]>s[i](f[i][j]+=sum)%=mod,(sum+=(f[i-1][j]-f[k][j]+mod)%mod)%=mod;k=(s.empty()?0:*s.rbegin()); sum=0;for(int j=0;j<26;j++)// s[i-1]<s[i](f[i][j]+=sum)%=mod,(sum+=(f[i-1][j]-f[k][j]+mod)%mod)%=mod; for(auto j:qr[i]) s.erase(s.find(j));for(auto j:qr2[i]) s2.erase(s2.find(j));for(auto j:ql[i]) s.insert(i);for(auto j:ql2[i]) s2.insert(i);}ll ans=0;for(int i=0;i<26;i++)(ans+=f[n][i])%=mod;cout<<ans;return 0;
}

T4 酒厂

link
思路
直接模拟肯定爆炸,应该可以想到 ds优化 。发现流出水的图像和酿酒的图像是很简单的。对于流出水的图像,一开始流入的水都会被酿酒,所以是一条水平直线,直到流入的水不能酿酒但可以流到下一个点,于是呈一条上升的一次函数,最后如果流入的水不能流到下一个点,图像又呈一条水平直线;对于酿酒图像,一开始多一点水就多一点酒,然后直到不管怎么加酒都不会酿成酒,图像没有变化。
所以发现对于一个区间 [ l , r ] [l,r] [l,r] 只要维护这些信息即可:

  • 可酿的酒量 a n s ans ans
  • 如果有 INF 的水从 l l l 流入,则可酿出的酒 m x i n mxin mxin
  • 如果有 INF 的水从 l l l 流入,尽量酿成酒后,可流到 r r r 的水 m x i n mxin mxin
  • r r r 处待流出的水 o u t out out

注意合并信息即可。可参考:this 。复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

代码

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;
const ll INF=1e18;int a[maxn],b[maxn];
ll c[maxn];
struct TREE{ ll ans,mxin,mxcan,out; }tree[maxn*4];
/*
ans:l~r 中酿的酒
mxin:l~r 中从 l 流入 INF 的水,可以酿出的酒
mxcan:l~r 中从 l 流入 INF 的水,可流到 r 的水量 
out:l~r 中在 r 出待流出的水量 
*/
void PushUp(int rt,int l,int r){int ls=rt*2,rs=rt*2+1,mid=(l+r)>>1;ll nw=min(c[mid],tree[ls].out);tree[rt].ans=tree[ls].ans+tree[rs].ans;if(nw>tree[rs].mxin){tree[rt].ans+=tree[rs].mxin;tree[rt].mxin=tree[ls].mxin;tree[rt].mxcan=min({tree[ls].mxcan,tree[rs].mxcan-min(tree[rs].mxcan,nw-tree[rs].mxin),c[mid]-nw});tree[rt].out=min(tree[rs].mxcan,nw-tree[rs].mxin)+tree[rs].out;}else{tree[rt].ans+=nw;tree[rt].mxin=min({tree[rs].mxin-nw,c[mid]-nw,tree[ls].mxcan})+tree[ls].mxin;tree[rt].mxcan=max(0ll,min(tree[rs].mxcan,min(tree[ls].mxcan,c[mid]-nw)-(tree[rs].mxin-nw)));tree[rt].out=tree[rs].out;}
}void Build(int rt,int l,int r){if(l==r){tree[rt].ans=min(a[l],b[l]);tree[rt].mxin=max(0,b[l]-a[l]);tree[rt].mxcan=INF;tree[rt].out=max(0,a[l]-b[l]);return;}int mid=(l+r)>>1;Build(rt*2,l,mid); Build(rt*2+1,mid+1,r);PushUp(rt,l,r);
}void Modify(int rt,int l,int r,int x){if(l==r){tree[rt].ans=min(a[l],b[l]);tree[rt].mxin=max(0,b[l]-a[l]);tree[rt].mxcan=INF;tree[rt].out=max(0,a[l]-b[l]);return;}int mid=(l+r)>>1;if(x<=mid) Modify(rt*2,l,mid,x);else Modify(rt*2+1,mid+1,r,x);PushUp(rt,l,r);
}int main(){freopen("wine.in","r",stdin);freopen("wine.out","w",stdout);int id,n,q; scanf("%d%d%d",&id,&n,&q);for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)scanf("%d",&b[i]);for(int i=1;i<=n;i++)scanf("%lld",&c[i]);Build(1,1,n); int p; ll x,y,z;while(q--){scanf("%d%lld%lld%lld",&p,&x,&y,&z);a[p]=x,b[p]=y,c[p]=z;Modify(1,1,n,p);printf("%lld\n",tree[1].ans);}return 0;
}
http://www.dtcms.com/a/504522.html

相关文章:

  • C 程序的现代方法
  • 学校网站设计论文商标注册网站缴费入口
  • 成都网站建设蜀美网络wordpress vip 插件
  • 怎么用jsp做网站泰州网站设计培训
  • 闵行做网站费用民治网站优化培训
  • 基于trae+花生壳实现网站的公网访问
  • 门户网站建设自查整改报告搜索引擎优化的专家是什么意思
  • 【GESP】C++四级真题 luogu-B4005 [GESP202406 四级] 黑白方块
  • 市场推广方案模板价格优化网站建设
  • 东莞市官网网站建设抚顺网站seo
  • Python数据挖掘之集成技术
  • 网站建设广东猎头公司收费
  • 【Android】BottomNavigationView实现底部导航栏
  • 小程序开发需要多少钱宁波seo外包服务商
  • XSS攻击防范:从入门到精通
  • 做云购网站网站框架怎么做
  • 建中英文网站网络优化公司有哪些
  • 详细讲解java中的方法(适合新手小白)
  • vip解析网站怎么做网络搜索词排名
  • 最新网站域名如何寻找网站建设需求客户
  • 河北网站建设推广如何制作活动宣传网页
  • Linux memfree 的计算逻辑
  • 宁波网站建设软件开发医药公司网站建设备案
  • 一 网站建设的目的和目标辽宁网站建站优化公司
  • 网站开发本地环境seo排名怎么做
  • 网站首页置顶是怎么做单页展示网站
  • 微信网页上的网站怎么做的网站响应时间方案
  • ScheduledExecutorService
  • 网站首页新世纪建设集团有限公司长沙网站seo收费
  • 网站优化的论文pc网站建设企业