20260528模拟赛
20260528模拟赛
丁香之路
题面:
一张 \(n\) 个点的完全图,边 \((i,j)\) 的边权是 \(|i-j|\),给出 \(m\) 条特殊边和起点 \(S\),对于每个 \(1\leq i\leq n\) 求出从 \(S\) 出发走到 \(i\) 结束,要求至少经过每个特殊边一次,边可以重复走,路径边权和的最小值。\(n\leq 2500\)
题解:
显然除了特殊边外一定是走若干相邻点之间的边把他们连接起来,考虑这些边怎么走最少,对于一个 \(t\),初始设置 \(s\to t\) 之间的边状态为 \(1\),表示他们全都要走,然后对于每一条特殊边 \((u,v)\),反转 \(u\to v\) 之间的所有边的状态,这样是最优的因为所有这些要走的边都不交。
但是此时这些边形成了若干连通块,我们需要把这些连通块连接起来,每次链接的代价是长度乘二,直接做最小生成树就好了。
代码
#include<bits/stdc++.h>
#define ll long long
#define fir first
#define sec second
using namespace std;
inline int read(){
int s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=2505;
int vis[N],a[N],b[N],n,m,S;
ll ans;
struct dsu{
int fa[N];
void init(int n){
for(int i=1;i<=n;i++) fa[i]=i;
}
int find(int x){
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return ;
fa[fx]=fy;
}
}A,B;
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();m=read();S=read();
A.init(n);vis[S]=1;
for(int i=1;i<=m;i++){
int u=read(),v=read();
if(u>v) swap(u,v);
a[u]^=1;a[v]^=1;
ans+=v-u;
vis[u]=1;vis[v]=1;
A.merge(u,v);
}
for(int i=1;i<=n;i++){
for(int i=1;i<=n;i++) B.fa[i]=A.fa[i],b[i]=a[i];
b[i]^=1;b[S]^=1;
for(int i=1;i<=n;i++) b[i]^=b[i-1];
ll res=ans;
for(int i=1;i<n;i++)
if(b[i]){
res++;
B.merge(i,i+1);
}
vector<tuple<int,int,int> >vec;
int lst=0;
for(int j=1;j<=n;j++)
if(vis[j]||j==i){
if(lst) vec.emplace_back(j-lst,lst,j);
lst=j;
}
sort(vec.begin(),vec.end());
for(auto o:vec){
int w,u,v;tie(w,u,v)=o;
int fx=B.find(u),fy=B.find(v);
if(fx!=fy){
B.fa[fx]=fy;
res+=w*2;
}
}
printf("%lld ",res);
}
return 0;
}
水池
题面:
有一个长条形的水池,可以划分成 \(n\) 格。其中第 \(i\) 格和 \(i+1\) 格之间相邻,由一块高度为 \(h_i\) 的可调节挡板隔开。第 \(1\) 格左侧和第 \(n\) 格右侧是无限高的池壁。初始时水池中没有水。现在进行 \(q\) 次操作,操作有以下四种:
- \(0\ i\ x\ h\) 在第 \(x\) 格灌水直到该格的水面高度不低于 \(h\)(若当前水面高度已经达到 \(h\) 则无事发生);
- \(1\ i\ x\) 打开第 \(x\) 格底部的排水口直到该格的水流干,再关闭排水口;
- \(2\ i\ x\ h\) 将第 \(x\) 格右侧的挡板高度增加到 \(h\)(不改变现有水面,保证挡板高度不会下降);
- \(3\ i\ x\) 查询第 \(x\) 格的水面高度。
其中,\(i\) 表示这次操作是基于第 \(i\) 次操作之后的情况。\(i=0\) 表示基于初始状态。也就是说,这个问题要求对操作可持续化。\(1\leq n,q\leq 200000\)
题解:
对于操作 0,是将从 \(x\) 开始向左第一块隔板高度 \(\geq h\) 的 \(L\) 和向右第一块隔板高度 \(\leq h\) 的 \(R\),这一区间赋值成 \(x\),但是 \(x\) 的水位若是已经大于 \(h\) 了那么就不进行操作了。对于操作 1,是将从 \(x\) 开始向左第一块隔板高度 \(\geq h\) 的 \(L\) 和向右第一块隔板高度 \(\leq h\) 的 \(R\),将从 \(L\) 到 \(R\) 的每一个格子的值赋成到 \(x\) 这一路径 (区间) 隔板的最大值。
实际维护水位是困难的,但是由于每次单点询问,可以维护每个点的水位是哪个操作最终导致的,这样如果最终导致操作是 \(0\),就输出它对应的 \(h\),如果最终操作是 \(1\),就输出那个时刻区间隔板的最大值。
所以一棵线段树维护最终导致操作是什么,区间覆盖,单点求值,可持久化。
另一棵维护隔板的变化,单点修改,区间查询 \(\max\),线段树二分向两侧第一个大于 \(h\) 的位置,可持久化。
代码
#include<bits/stdc++.h>
#define ll long long
#define fir first
#define sec second
using namespace std;
inline int read(){
int s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=2e5+5;
int n,Q;
struct que{
int op,x,v;
}qu[N];
namespace H{
int rt[N],tot;
struct Tree{int l,r,v;};
vector<Tree>t(1);
void pushup(int s){
t[s].v=max(t[t[s].l].v,t[t[s].r].v);
}
int build(int l,int r){
int s=++tot;
t.emplace_back(Tree());
if(l==r){
t[s].v=read();
return s;
}
int mid=l+r>>1;
int L=build(l,mid);
int R=build(mid+1,r);
t[s].l=L; t[s].r=R;
pushup(s);
return s;
}
void build(){ rt[0]=build(1,n-1); }
int update(int u,int l,int r,int x,int v){
int s=++tot;
t.emplace_back(t[u]);
t[s].v=max(t[s].v,v);
if(l==r) return s;
int mid=l+r>>1,L,R;
if(x<=mid) L=update(t[u].l,l,mid,x,v),t[s].l=L;
else R=update(t[u].r,mid+1,r,x,v),t[s].r=R;
return s;
}
void update(int i,int x,int w){
rt[i]=update(rt[i],1,n-1,x,w);
}
int erfenl(int s,int l,int r,int x,int v){
if(t[s].v<v) return 0;
if(l==r) return l;
int mid=l+r>>1,ans=0;
if(x>mid) ans=erfenl(t[s].r,mid+1,r,x,v);
if(ans==0) ans=erfenl(t[s].l,l,mid,x,v);
return ans;
}
int erfenr(int s,int l,int r,int x,int v){
if(t[s].v<v) return n+1;
if(l==r) return l;
int mid=l+r>>1,ans=n+1;
if(x<=mid) ans=erfenr(t[s].l,l,mid,x,v);
if(ans==n+1) ans=erfenr(t[s].r,mid+1,r,x,v);
return ans;
}
pair<int,int> erfen(int i,int x,int v){
int l,r;
if(x==1) l=1;
else l=erfenl(rt[i],1,n-1,x-1,v)+1;
if(x==n) r=n;
else r=erfenr(rt[i],1,n-1,x,v);
return make_pair(l,r);
}
int query(int s,int l,int r,int L,int R){
if(L<=l&&r<=R) return t[s].v;
int mid=l+r>>1;
if(R<=mid) return query(t[s].l,l,mid,L,R);
if(L>mid) return query(t[s].r,mid+1,r,L,R);
return max(query(t[s].l,l,mid,L,R),query(t[s].r,mid+1,r,L,R));
}
int query(int i,int L,int R){
return query(rt[i],1,n-1,L,R);
}
}
namespace T{
int rt[N],tot;
struct Tree{ int l,r,v;};
vector<Tree>t(1);
int update(int u,int l,int r,int L,int R,int v){
int s=++tot;
t.emplace_back(t[u]);
if(L<=l&&r<=R){
t[s].v=v;
return s;
}
int mid=l+r>>1,ls,rs;
if(L<=mid) ls=update(t[u].l,l,mid,L,R,v),t[s].l=ls;
if(R>mid) rs=update(t[u].r,mid+1,r,L,R,v),t[s].r=rs;
return s;
}
void update(int i,int L,int R,int v){
rt[i]=update(rt[i],1,n,L,R,v);
}
int query(int s,int l,int r,int x){
if(l==r) return t[s].v;
int mid=l+r>>1;
if(x<=mid) return max(t[s].v,query(t[s].l,l,mid,x));
else return max(t[s].v,query(t[s].r,mid+1,r,x));
}
int query(int i,int x){ return query(rt[i],1,n,x); }
}
int query(int rt,int x){
int i=T::query(rt,x);
if(i==0) return 0;
if(qu[i].op==0) return qu[i].v;
else{
if(x==qu[i].x) return 0;
int l=x,r=qu[i].x;
if(l>r) swap(l,r);
return H::query(i,l,r-1);
}
}
void upd(int i,int x,int v){
int l,r;tie(l,r)=H::erfen(i,x,v);
T::update(i,l,r,i);
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();Q=read();
if(n!=1) H::build();
for(int i=1;i<=Q;i++){
int op=read(),pre=read();
H::rt[i]=H::rt[pre];
T::rt[i]=T::rt[pre];
if(op==0){
int x=read(),v=read();
int w=query(i,x);
if(w<v) upd(i,x,v);
qu[i]={op,x,v};
}
else if(op==1){
int x=read();
int v=query(i,x);
upd(i,x,v);
qu[i]={op,x,0};
}
else if(op==2){
int x=read(),v=read();
H::update(i,x,v);
}
else{
int x=read();
printf("%d\n",query(i,x));
}
}
return 0;
}

浙公网安备 33010602011771号