2025-11-08 NOIP 模拟赛4 赛后总结

Record

  • 8:06 会了 T1。特殊性质立大功。
  • 8:22 过掉 T1 大洋里。开 T2。
  • 8:30 没有任何思路。
  • 9:16 思考 T2 思考了一个小时但还是没有任何头绪。放弃 T2。听说 T3 比 T2 可做。
  • 10:23 写完 T3 了。直接过掉大洋里。
  • 11:36 拍了拍 T3,发现了一个细节错误。这下应该没错了。
  • (忘了几点)开 T4。打了个表然后啥也看不出来。遂放弃。

最后是 100+18+100+0。

T1 限速

题意

给定一张图,求这张图的生成树中权值最小的一个。

定义生成树的权值为:

  • 若树中最大权值的边 \(w_{\max}\le k\),则权值为 \(k-w_{\max}\)
  • 否则权值为 \(\sum_{w_i\ge k}|w_i-k|\)

赛时

对于两个特殊性质,也就是 \(w_i\le k\)\(w_i\ge k\) 而言,都是简单的。

所以考虑把两种情况合起来考虑,所以做完了。

题解

首先将原图所有边权 \(\ge k\) 的边去掉,跑一次 Kruskal。

如果图已经连通了,那么我们再把小于等于 \(k\)\(w_{\max}\) 这条边加上去,那么答案就是 \(k-w_{\max}\)

另一种情况,我们可以选择 \(\gt k\) 的最小边,让这棵树计算答案变成第二种。所以这种情况的答案就是两种取 \(\min\)

如果图不连通,那么我们再在刚才的图的基础上加上 \(\gt k\) 的边再跑一次 Kruskal。然后答案计算就很显然了。

哦对了,并查集用了一个黑科技。想了解的可以去看这篇文章。或者是搬运到ZYZOJ上的版本

时间复杂度 \(O(m\log m+m\alpha(n))\)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;

int n,m;
long long k;

struct node{
    int x,y;
    long long v;
    bool operator<(const node&_Q)const{return v<_Q.v;}
};
vector<node> v_le,v_gt;

int fa[200010],siz[200010];
void pathzip(int&x){while(fa[x]!=x)x=fa[x]=fa[fa[x]];} // 黑科技

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    for(int i=1;i<=m;i++){
        int x,y;cin>>x>>y;
        long long v;cin>>v;
        if(v<=k) v_le.push_back({x,y,v});
        else v_gt.push_back({x,y,v});
    }

    int addcnt=0;
    long long v_le_max=-infll;
    for(auto pr:v_le){
        int x=pr.x;
        int y=pr.y;
        pathzip(x);
        pathzip(y);
        if(x!=y){
            if(siz[x]>siz[y]) swap(x,y);
            fa[x]=y,siz[y]+=siz[x];
            addcnt++;
        }
        v_le_max=max(v_le_max,pr.v);
    }

    sort(v_gt.begin(),v_gt.end());
    if(addcnt==n-1){
        long long ans=0;
        if(v_gt.size()==0) ans=k-v_le_max;
        else ans=min(k-v_le_max,v_gt[0].v-k);
        cout<<ans<<"\n";
    }else{
        long long ans=0;
        for(auto pr:v_gt){
            int x=pr.x;
            int y=pr.y;
            pathzip(x);
            pathzip(y);
            if(x!=y){
                if(siz[x]>siz[y]) swap(x,y);
                fa[x]=y,siz[y]+=siz[x];
                ans+=pr.v-k;
            }
        }
        cout<<ans<<"\n";
    }
    
    # ifndef ONLINE_JUDGE
    cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
    # endif
    return 0;
}

T3 单峰数列(别样的分块大战。)

题意

维护一个序列,支持以下操作:

  • 操作 \(1\):将在区间 \([l,r]\) 中的数加上 \(x\)
  • 操作 \(2\):查询区间 \([l,r]\) 的所有数是否全都相等。
  • 操作 \(3\):查询区间 \([l,r]\) 是否严格单调递增。
  • 操作 \(4\):查询区间 \([l,r]\) 是否严格单调递减。
  • 操作 \(5\):查询区间 \([l,r]\) 是否呈单峰,即存在一个位置 \(l\lt p\lt r\)\([l,p]\) 严格单调递增,\([p,r]\) 严格单调递减。

赛时

T3 比 T2 可做是真的。

题解

序列问题,一眼神秘数据结构。

但是线段树似乎没办法直接维护这东西。

所以考虑分块。


其实存在非常天才的线段树维护差分的做法,但是我赛时没想出来,所以这里介绍我的神秘分块做法。


给每个块三个标签,分别代表块内所有元素满足全部相等、单调递增、单调递减。

然后带上懒标记就解决了前四个操作。

考虑操作 \(5\),不难注意到,位置 \(p\) 是且仅能是区间内的最大值。

所以再多维护一个区间最大值和对应的位置,则操作 \(5\) 成立的条件为 \([l,p]\) 满足操作 \(3\)\([p,r]\) 满足操作 \(4\)

因为懒了所以查询区间最大值直接上了线段树。

时间复杂度 \(O(q\log n+q\sqrt{n})\)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;

class segmenttree{
    private:
        struct node{
            int l,r;
            pair<long long,int> val_max;
            long long lz_add;
        }t[400010];
    
    public:
        void pushdown(int p){
            if(!t[p].lz_add) return;

            t[p<<1].lz_add+=t[p].lz_add;
            t[p<<1|1].lz_add+=t[p].lz_add;
            t[p<<1].val_max.first+=t[p].lz_add;
            t[p<<1|1].val_max.first+=t[p].lz_add;

            t[p].lz_add=0;
        }
        void pushup(int p){
            t[p].val_max=max(t[p<<1].val_max,t[p<<1|1].val_max);
        }

        void build(int l,int r,long long *a,int p=1){
            t[p]={l,r,{-infll,0},0};
            if(l==r) t[p].val_max={a[l],l};
            else{
                int mid=(l+r)>>1;
                build(l,mid,a,p<<1);
                build(mid+1,r,a,p<<1|1);
                pushup(p);
            }
        }

        void modify(int l,int r,long long v,int p=1){
            if(l<=t[p].l&&t[p].r<=r){
                t[p].lz_add+=v;
                t[p].val_max.first+=v;
            }else{
                pushdown(p);
                int mid=(t[p].l+t[p].r)>>1;
                if(l<=mid) modify(l,r,v,p<<1);
                if(r>mid) modify(l,r,v,p<<1|1);
                pushup(p);
            }
        }

        pair<long long,int> query(int l,int r,int p=1){
            if(l<=t[p].l&&t[p].r<=r) return t[p].val_max;
            pushdown(p);
            int mid=(t[p].l+t[p].r)>>1;
            pair<long long,int> res={-infll,0};
            if(l<=mid) res=max(res,query(l,r,p<<1));
            if(r>mid) res=max(res,query(l,r,p<<1|1));
            return res;
        }
};
segmenttree segt;

int n,q;
long long a[100010];

int block_length,belong[100010],block_first[320];

long long block_lz_add[320];
int block_tag[320];

void pushdown(int x){
    if(!block_lz_add[x]) return;
    for(int i=block_first[x];i<block_first[x+1];i++) a[i]+=block_lz_add[x];
    block_lz_add[x]=0;
}

void rebuild(int x){
    bool is_inc=true;
    bool is_dec=true;
    bool is_allsame=true;
    for(int p=block_first[x]+1;p<block_first[x+1];p++){
        is_inc&=(a[p-1]<a[p]);
        is_dec&=(a[p-1]>a[p]);
        is_allsame&=(a[p-1]==a[p]);
    }
    if(is_inc) block_tag[x]=1;
    else if(is_dec) block_tag[x]=2;
    else if(is_allsame) block_tag[x]=3;
    else block_tag[x]=0;
}

void operation1(int l,int r,long long x){
    if(belong[l]==belong[r]){
        for(int i=l;i<=r;i++) a[i]+=x;
        pushdown(belong[l]);
        rebuild(belong[l]);
    }else{
        for(int i=l;i<block_first[belong[l]+1];i++) a[i]+=x;
        for(int i=belong[l]+1;i<belong[r];i++) block_lz_add[i]+=x;
        for(int i=block_first[belong[r]];i<=r;i++) a[i]+=x;
        pushdown(belong[l]);
        pushdown(belong[r]);
        rebuild(belong[l]);
        rebuild(belong[r]);
    }
}

bool operation2(int l,int r){
    bool is_allsame=true;
    if(belong[l]==belong[r]){
        pushdown(belong[l]);
        rebuild(belong[l]);
        for(int i=l+1;i<=r;i++) is_allsame&=(a[i-1]==a[i]);
    }else{
        pushdown(belong[l]);
        pushdown(belong[r]);
        rebuild(belong[l]);
        rebuild(belong[r]);
        long long same_value=a[l];
        for(int i=l;i<block_first[belong[l]+1];i++) is_allsame&=(a[i]==same_value);
        for(int i=belong[l]+1;i<belong[r];i++) is_allsame&=(block_tag[i]==3&&block_lz_add[i]+a[block_first[i]]==same_value);
        for(int i=block_first[belong[r]];i<=r;i++) is_allsame&=(a[i]==same_value);
    }
    return is_allsame;
}

bool operation3(int l,int r){
    bool is_inc=true;
    if(belong[l]==belong[r]){
        pushdown(belong[l]);
        rebuild(belong[l]);
        for(int i=l+1;i<=r;i++) is_inc&=(a[i-1]<a[i]);
    }else{
        pushdown(belong[l]);
        pushdown(belong[r]);
        rebuild(belong[l]);
        rebuild(belong[r]);
        for(int i=l+1;i<block_first[belong[l]+1];i++) is_inc&=(a[i-1]<a[i]);
        for(int i=belong[l]+1;i<belong[r];i++) is_inc&=(block_tag[i]==1&&block_lz_add[i-1]+a[block_first[i]-1]<block_lz_add[i]+a[block_first[i]]);
        is_inc&=(block_lz_add[belong[r]-1]+a[block_first[belong[r]]-1]<a[block_first[belong[r]]]);
        for(int i=block_first[belong[r]]+1;i<=r;i++) is_inc&=(a[i-1]<a[i]);
    }
    return is_inc;
}

bool operation4(int l,int r){
    bool is_dec=true;
    if(belong[l]==belong[r]){
        pushdown(belong[l]);
        rebuild(belong[l]);
        for(int i=l+1;i<=r;i++) is_dec&=(a[i-1]>a[i]);
    }else{
        pushdown(belong[l]);
        pushdown(belong[r]);
        rebuild(belong[l]);
        rebuild(belong[r]);
        for(int i=l+1;i<block_first[belong[l]+1];i++) is_dec&=(a[i-1]>a[i]);
        for(int i=belong[l]+1;i<belong[r];i++) is_dec&=(block_tag[i]==2&&block_lz_add[i-1]+a[block_first[i]-1]>block_lz_add[i]+a[block_first[i]]);
        is_dec&=(block_lz_add[belong[r]-1]+a[block_first[belong[r]]-1]>a[block_first[belong[r]]]);
        for(int i=block_first[belong[r]]+1;i<=r;i++) is_dec&=(a[i-1]>a[i]);
    }
    return is_dec;
}

bool operation5(int l,int r){
    pair<long long,int> wmax=segt.query(l,r);
    if(wmax.second==l||wmax.second==r) return false;
    return operation3(l,wmax.second)&&operation4(wmax.second,r);
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];

    block_length=max((int)sqrt(n),1);
    for(int i=1;i<=n;i++){
        belong[i]=(i-1)/block_length+1;
        if(!block_first[belong[i]]) block_first[belong[i]]=i;
    }
    block_first[belong[n]+1]=n+1;

    for(int i=1;i<=belong[n];i++){
        bool is_inc=true;
        bool is_dec=true;
        bool is_allsame=true;
        for(int p=block_first[i]+1;p<block_first[i+1];p++){
            is_inc&=(a[p-1]<a[p]);
            is_dec&=(a[p-1]>a[p]);
            is_allsame&=(a[p-1]==a[p]);
        }
        if(is_inc) block_tag[i]=1;
        else if(is_dec) block_tag[i]=2;
        else if(is_allsame) block_tag[i]=3;
        else block_tag[i]=0;
    }

    segt.build(1,n,a);

    cin>>q;
    while(q--){
        int op,l,r;cin>>op>>l>>r;
        if(op==1){
            long long x;cin>>x;
            operation1(l,r,x);
            segt.modify(l,r,x);
        }
        else if(op==2) cout<<operation2(l,r)<<"\n";
        else if(op==3) cout<<operation3(l,r)<<"\n";
        else if(op==4) cout<<operation4(l,r)<<"\n";
        else if(op==5) cout<<operation5(l,r)<<"\n";
    }
    
    # ifndef ONLINE_JUDGE
    cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
    # endif
    return 0;
}

总结

T2 是什么啥比 Ad-hoc。。我为什么看不懂题解在说啥。。

T4 不会。

posted @ 2025-11-09 16:25  AeeE5x  阅读(24)  评论(0)    收藏  举报