多校A层冲刺NOIP2024模拟赛20

简评:新拉的💩,热乎的。

星际联邦

简要题意

两个点\(i,j(1\le i<j\le n,n\le 10^5)\)之间连边的代价是\(a_j-a_i(\forall i\in [1,n],-300000\le a_i\le 300000)\),求最小生成树。

赛时经历

先打了一个假的(其实是不完整)贪心,然后过掉了小样例,被大样例卡掉了,然后打了个暴力开始拍,一步步完善贪心策略。过拍时大概一个小时后了,比人均晚,太菜了。过拍的时候写的是用堆维护的后缀min,是\(O(n\log n)\),交了一发过了。T2没思路就又回来看了一眼,发现堆没有啥用,改了一下就写成\(O(n)\)的了。

solution

std是\(O(n\log n)\)的,好像爆标了。std是\(prim\)做法或\(Borůvka\)做法我不会

考虑到一个点要么与严格前缀最大值连边,要么与其后边的最小值连边。如果该点就是非严格前缀最大值,那么一定为其后面的最小值与其严格前缀最大值连边,时间复杂度\(O(n)\)

证明的话考虑对拍,拍不出错就是对的,考虑贪心,从后往前连肯定与前缀最大值连更优,如果本身就是前缀最大值,那么由于后面的点都和其形成了一个连通块,直接取最小值即可。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
    FILE *InFile = stdin,*OutFile = stdout;
    // FILE *InFile = freopen("star.in","r",stdin),*OutFile = freopen("star.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 3e5 + 10;
int n,a[N],dmx[N],pmx[N],mn = INT_MAX;
bitset<N> pd;
inline void solve(){
    cin>>n;rep(i,1,n,1) cin>>a[i];
    pmx[0] = -INT_MAX;
    rep(i,1,n,1){
        pmx[i] = pmx[i-1];
        if(a[i] >= pmx[i]) pd.set(i),pmx[i] = a[i];
    }
    ll ans = 0;
    drep(i,n,2,1){
        mn = min(a[i],mn);
        if(pd[i]) ans += mn - pmx[i-1];
        else ans += min(a[i] - pmx[i - 1],mn - a[i]);
    }
    cout<<ans;
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}

和平精英

什么离谱名字

简要题意

给定一个长度为\(n(n\le 10^5)\)的序列\(a(0\le a_i<2^30)\),有\(q(q\le 10^5)\)次询问,每次询问给定一个区间\(l,r\),问可否将\(a_{l\sim r}\)分成两部分,使得一部分的按位或和另一部分的按位与相等。

赛时经历

打了个\(O(n\times \min\{n,V\})\)的,期望\(60\),实际\(34\),乐。

和正解除了\(popcount\)都想到了,甚至还想过拆位,对着T2看了一整场,连T3,T4暴力都没打其实是懒得打

solution

学的多校老哥在线的\(O(q\log n\log V)\),其实和正解差不多,就是直接用线段树维护了一下。

容易发现一个结论,考虑枚举按位与和按位或的值,设为\(val\),那么所有\(a_i<val\)都应该放在\(or\)中,\(a_i>val\)都放在\(and\)中,暴力check即可。可以做到\(O(n^2)/O(n^2\log n)\)。证明的话,考虑\(a|b\ge \max\{a,b\},a\&b\le\min\{a,b\}\)即可。

接着沿用上面那个思路,考虑按位与和按位或还可以使什么满足上面那个\(a|b\ge \max\{a,b\},a\&b\le\min\{a,b\}\)性质,容易想到\(popcount\)。考虑枚举答案的\(popcount\),记为\(p\),所以将所有\(popcount(a_i)<p\)放入\(or\)中,\(popcount(a_i)>b\)放入\(and\)中,如果\(popcount(a_i)=p\),当且仅当所有的\(popcount(a_i)=p\)\(a_i\)相等时有解,类比上面的性质证明即可。

然后对每个\(popcount\)维护线段树即可,空间复杂度\(O(n\log V)\)

但是常数比正解的离线分治要大,所以赛时应略带卡常但赛后开3s了,不卡了

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
    // FILE *InFile = stdin,*OutFile = stdout;
    FILE *InFile = freopen("peace.in","r",stdin),*OutFile = freopen("peace.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 1e5 + 10;
int n,q,a[N],sor[31],sand[31],sum[31];
struct node{
    int v1,v2,s;
    node(){}
    node(int q,int w,int e):v1(q),v2(w),s(e){}
    inline node operator + (const node &x){return node(v1|x.v1,v2&x.v2,s+x.s);}
}x[31];
struct Segment_Tree{
    struct segment_tree{
        int l,r;node v;
        #define l(x) tree[x].l
        #define r(x) tree[x].r
        #define v(x) tree[x].v
    }tree[N];
    inline void P(int k){v(k) = v(k<<1) + v(k<<1|1);}
    void B(int k,int l,int r){
        l(k) = l,r(k) = r;
        if(l == r) return v(k) = node(0,(1<<30)-1,0),void();
        int mid = (l + r) >> 1;
        B(k<<1,l,mid);B(k<<1|1,mid+1,r);P(k);
    }
    void upd(int k,int pos,node v){
        if(l(k) == r(k)) return v(k) = v(k)+v,void();
        int mid = (l(k) + r(k)) >> 1;
        if(pos <= mid) upd(k<<1,pos,v);else upd(k<<1|1,pos,v);
        P(k);
    }
    node qry(int k,int l,int r){
        if(l <= l(k) && r(k) <= r) return v(k);
        int mid = (l(k) + r(k)) >> 1;
        node res = node(0,(1<<30)-1,0);
        if(l <= mid) res = res + qry(k<<1,l,r);
        if(r > mid) res = res + qry(k<<1|1,l,r);
        return res;
    }
}T[31];
inline int ppc(int x){int res = 0;while(x) x -= (x&(-x)),res++;return res;}
inline void solve(){
    cin>>n>>q;rep(i,1,n,1) cin>>a[i];
    rep(i,0,30,1) T[i].B(1,1,n);
    rep(i,1,n,1) T[ppc(a[i])].upd(1,i,node(a[i],a[i],1));
    rep(test,1,q,1){
        int l,r;cin>>l>>r;
        rep(i,0,30,1) x[i] = T[i].qry(1,l,r);
        sor[0] = x[0].v1; rep(i,1,30,1) sor[i] = sor[i-1] | x[i].v1;
        sand[30] = x[30].v2;drep(i,29,0,1) sand[i] = sand[i+1] & x[i].v2;

        sum[0] = x[0].s;  rep(i,1,30,1) sum[i] = sum[i-1] + x[i].s;
        bool flag = false;
        rep(i,0,30,1) if(sum[i] && sum[30] - sum[i]) flag |= sor[i] == sand[i + 1];

        rep(i,0,30,1) if(x[i].v1 == x[i].v2 && x[i].s > 1) flag |= sor[i] == sand[i];
        cout<<(flag?"YES\n":"NO\n");
    }
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}

打ABC和分块去了,T3,T4明天再调。

p

image
image

posted @ 2024-11-09 18:40  CuFeO4  阅读(44)  评论(0编辑  收藏  举报