sm2025 模拟赛11 (2025.10.5)
文章目录
- T1 厚牛堡(easy,100%)
- T2 旅行商(mid,80%)
- T3 登山(hard-,5%)
- T4 樱之艺术家
T1 厚牛堡(easy,100%)
link
思路
易知传播天数越大,所需奶牛越少。于是我们找到每个连续段最大的可能的传播天数取最小值,直接计算即可。当然要注意对在序列两端的段特别考虑。
T2 旅行商(mid,80%)
link
思路
首先考虑如何判 −1-1−1 ,就是那些不能到达环的点,拓扑找出度为 000 的点即可。
易知如果资产 ≥\ge≥ 环上边的最大限制,那么就一定可以在这个环上不停地走。设 ansians_iansi 为从 iii 出发的答案。对于一条边 (u,v,r,p)(u,v,r,p)(u,v,r,p) ,会有 ansu=min(ansu,min(r,ansv−p))ans_u=min(ans_u,min(r,ans_v-p))ansu=min(ansu,min(r,ansv−p)) ,但是由于不是 DAG ,转移会有后效性。但我们发现 ansuans_uansu 会有上限,即总 ≤\le≤ 所在环上的最大的 rrr 。并且如果从点的角度会有后效性,但每条边只会贡献一次答案。于是建反图,考虑 rrr 从大到小枚举转移的边,即一开始假设带了 rrr 元出发,这样一定合法,转移完一条边就删去它,这样图就能为 DAG 了,可以拓扑解决。可以保证最终答案不仅合法还能最优。
反思
场上想到一种做法,但不保证正确性。
因为知道求 −1-1−1 的时候要判环,我们想到 SCC ,将 −1-1−1 的点踢掉。对剩下的图缩点,得到一个 DAG (树)。注意到叶子结点原来一定为环,所以这些点只能永远在这个环上走,所以可以用最短路求出这个环上的点的 ansansans 。然后往上转移。非叶子节点可以在环上走也可以往下走,所以同样可以先最短路求出只在环上不停走时的 ansansans 。
不过这样好像一直都消除不了环的影响,还是应该不断删边变成 DAG 然后拓扑做。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;int idx,head[maxn];
struct EDGE{ int v,next,id; ll ct,w; }e[maxn];
void Insert(int u,int v,ll ct,ll w,int id){e[++idx]={v,head[u],id,ct,w};head[u]=idx;
}int out[maxn];
ll ans[maxn];
bool vis[maxn];
struct EDGE2{ int x,y,id; ll ct,w; }eg[maxn];
queue<int>q;
int main(){int n,m; cin>>n>>m;for(int i=1;i<=m;i++){cin>>eg[i].x>>eg[i].y>>eg[i].ct>>eg[i].w; eg[i].id=i;out[eg[i].x]++;Insert(eg[i].y,eg[i].x,eg[i].ct,eg[i].w,eg[i].id);}for(int i=1;i<=n;i++)ans[i]=1e9;for(int i=1;i<=n;i++)if(!out[i]) q.push(i);sort(eg+1,eg+m+1,[](EDGE2 a,EDGE2 b){ return a.ct>b.ct; });for(int i=1;i<=m;i++){while(!q.empty()){int x=q.front();q.pop();for(int i=head[x];i;i=e[i].next){if(vis[e[i].id]) continue;vis[e[i].id]=1;int v=e[i].v;out[v]--;if(!out[v]) q.push(v);if(ans[x]!=1e9) ans[v]=min(ans[v],max(e[i].ct,ans[x]-e[i].w));}}if(!vis[eg[i].id]){vis[eg[i].id]=1;out[eg[i].x]--;if(!out[eg[i].x]) q.push(eg[i].x);ans[eg[i].x]=min(ans[eg[i].x],eg[i].ct);}}for(int i=1;i<=n;i++)cout<<(ans[i]==1e9?-1:ans[i])<<" ";return 0;
}
T3 登山(hard-,5%)
link
orzsst
思路
设每座山初始高度为 aia_iai 。
首先考虑最终每座山有多高,有 hi=min(maxj=1iaj,maxj=inaj)h_i=min(\max\limits_{j=1}^{i} a_j,\max\limits_{j=i}^{n} a_j)hi=min(j=1maxiaj,j=imaxnaj) 。
即山峰一定为序列最大值,设位置为 xxx 。那么在 [1,x)[1,x)[1,x) 中找最大值,记下标为 ppp,i∈[p,x),hi=hpi \in [p,x),h_i=h_pi∈[p,x),hi=hp 。然后继续在 [1,p)[1,p)[1,p) 中找最大值,重复做即可。右边 (x,n](x,n](x,n] 同理。会发现只有这一种情况合法。
现在考虑如何将 aia_iai -> hih_ihi 。
首先我们肯定要在 [1,i)[1,i)[1,i) 和 (i,n](i,n](i,n] 中分别找一个最小的比 aia_iai 大的数当作代价中的 h[i],h[k]
,这样肯定最优,但是这样 aia_iai 的变化会对取的数有不断影响,仍不好做。于是我们希望相同值的 iii 一起处理到下一个有意义的值,这样就避免了取的数会不断变化,但是我们还是不知道要怎么做。
继续发现性质,如果有 ai<aja_i \lt a_jai<aj ,那肯定是优先修改 aia_iai 直到 ai>aja_i \gt a_jai>aj 。
当有一堆数满足 ai=aja_i=a_jai=aj 时,设这堆数最小的位置为 lll ,最大位置为 rrr ,会想到先将 ala_lal 和 ara_rar 都 +1+1+1 ,然后让中间的数分别 +1+1+1 ,这样一直更改到目标值肯定是最优的,具体 al,ara_l,a_ral,ar 二者 +1+1+1 的先后顺序比较一下就好。
那么就是先从小到大枚举 aia_iai 的值域 VVV(个数为 O(n)O(n)O(n)),每次将等于这个值的 aia_iai 加入 set 中,可知此时 set 中的所有数都等于这个值,那么就将 set 中的所有数变到 VVV 中的下一个值(这个代价推一下式子就好),如果 set 中的某些数已经达到最终高度 hih_ihi 了就扔出 set 。这样最终所有数都会达到相应的 hih_ihi 。
另外,如何在一段区间 [l,r][l,r][l,r] 中找到最小的大于 xxx 的数。可以考虑用线段树维护。在将 aia_iai 加入 set 的时候,就意味着 ai≤a_i\leai≤ 当前的值,将 aia_iai 改为 INF 即可,那剩下的不是 INF 的数就都 >\gt> 当前值,查询就是查区间最小值。
代码
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+5;
const ll INF=2e9,mod=1e6+3;int a[maxn];
struct TREE{ ll mi; }tree[maxn*4];
void Build(int rt,int l,int r){if(l==r){tree[rt].mi=a[l];return;}int mid=(l+r)>>1;Build(rt*2,l,mid); Build(rt*2+1,mid+1,r);tree[rt].mi=min(tree[rt*2].mi,tree[rt*2+1].mi);
}void Modify(int rt,int l,int r,int x,ll y){if(l==r){tree[rt].mi=y;return;}int mid=(l+r)>>1;if(x<=mid) Modify(rt*2,l,mid,x,y);else Modify(rt*2+1,mid+1,r,x,y);tree[rt].mi=min(tree[rt*2].mi,tree[rt*2+1].mi);
}ll Query(int rt,int l,int r,int x,int y){if(x>y) return INF;if(x<=l&&r<=y) return tree[rt].mi;int mid=(l+r)>>1; ll s=INF;if(x<=mid) s=min(s,Query(rt*2,l,mid,x,y));if(y>mid) s=min(s,Query(rt*2+1,mid+1,r,x,y));return s;
}int h[maxn];
ll a2[maxn];
set<int>st;
vector<int>vt[maxn],vt2[maxn];
int main(){freopen("hike.in","r",stdin);freopen("hike.out","w",stdout);int n; scanf("%d",&n); int ma=0;for(int i=1;i<=n;i++){scanf("%lld",&a[i]); a2[i]=a[i];//! if(a[ma]<a[i]) ma=i;} for(int i=1;i<=ma;i++)h[i]=max(h[i-1],a[i]);for(int i=n;i>ma;i--)h[i]=max(h[i+1],a[i]);sort(a2+1,a2+n+1);int m=unique(a2+1,a2+n+1)-a2-1;for(int i=1;i<=n;i++){int ph=lower_bound(a2+1,a2+m+1,a[i])-a2;int ph2=lower_bound(a2+1,a2+m+1,h[i])-a2;vt[ph].push_back(i);vt2[ph2].push_back(i);}Build(1,1,n);ll ans=0;for(int i=1;i<m;i++){for(auto j:vt[i]){st.insert(j);Modify(1,1,n,j,INF);}for(auto j:vt2[i])st.erase(j);if(st.empty()) continue;int l=*st.begin(),r=*st.rbegin();ll x=Query(1,1,n,1,l-1),y=Query(1,1,n,l+1,n),p=Query(1,1,n,1,r-1),q=Query(1,1,n,r+1,n);ll len=st.size(),s=a2[i+1]-a2[i];if(len==1) (ans+=(x+y)*s%mod)%=mod,(ans+=(a2[i]+a2[i+1]-1)*s/2%mod)%=mod;else{(ans+=(x+q+min(y,p))*s%mod)%=mod;(ans+=(a2[i]+1+a2[i+1])*s/2%mod*(2*len-3)%mod)%=mod;(ans+=(a2[i]+a2[i+1]-1)*s/2%mod*len%mod)%=mod;}}printf("%lld",ans);return 0;
}
T4 樱之艺术家
link
不会啊