NOIP 模拟 8

A 星际联邦

直接贪,对于每个点,连前缀 max,后缀 min,再把前缀 max 和后缀 min 连,直接跑 kruskal 就行,因为对 \(i\) 连,确保了最小,然后再连确保了连通性。
正解是无脑菠萝,维护不在同一连通块的最值和次值就行。

#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=3e5+10,inf=1e18;
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,tot,a[N],ans;
struct BINGCHAJI{
    int fa[N],size[N];
    inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline void merge(int x,int y){x=find(x),y=find(y);if(size[x]>size[y])std::swap(x,y);fa[x]=y;}
}d;
struct weihu{
    int w1,u1,w2,u2;
    inline void merge_max(int w,int u){
        if(!u1){w1=w,u1=u;return;}
        if(w>w1){if(u!=u1)w2=w1,u2=u1;w1=w,u1=u;return;}
        if(!u2){w2=w,u2=u;return;}
        if(w>w2&&u!=u1){w2=w,u2=u;return;}
    }
    inline void merge_min(int w,int u){
        if(!u1){w1=w,u1=u;return;}
        if(w<w1){if(u!=u1)w2=w1,u2=u1;w1=w,u1=u;return;}
        if(!u2){w2=w,u2=u;return;}
        if(w<w2&&u!=u1){w2=w,u2=u;return;}
    }
};
struct bian{
    int v,w;
    inline void merge(int v1,int w1){if(w1<w||!v)v=v1,w=w1;}
}b[N];
inline void Boruvka(){
    for(int i=1;i<=n;++i)b[i]={0,inf};
    weihu max={0,0,0,0},min={0,0,0,0};
    for(int i=2;i<=n;++i){
        max.merge_max(a[i-1],d.find(i-1));
        if(max.u1&&max.u1!=d.find(i))b[d.find(i)].merge(max.u1,a[i]-max.w1);
        if(max.u2&&max.u2!=d.find(i))b[d.find(i)].merge(max.u2,a[i]-max.w2);
    }
    for(int i=n-1;i;--i){
        min.merge_min(a[i+1],d.find(i+1));
        if(min.u1&&min.u1!=d.find(i))b[d.find(i)].merge(min.u1,min.w1-a[i]);
        if(min.u2&&min.u2!=d.find(i))b[d.find(i)].merge(min.u2,min.w2-a[i]);
    }
    for(int i=1;i<=n;++i)if(d.find(i)==i&&b[i].v&&d.find(b[i].v)!=i){
        ans+=b[i].w,d.merge(b[i].v,i),tot++;
    }
}
signed main(){
    freopen("star.in","r",stdin);freopen("star.out","w",stdout);
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    n=read();for(int i=1;i<=n;++i)a[i]=read(),d.fa[i]=i,d.size[i]=1;
    while(tot<n-1)Boruvka();std::cout<<ans<<'\n';
}

B 和平精英

傻逼题,首先必要条件是,popcount 大于答案的为与,小于的为或,等于的看情况分配,枚举答案的 popcount,然后用线段树或者 st 表维护相关信息就行了,注意 popcount 相等的值也要相等,没有大于的看能不能用等于的分配,具体来说,就是维护区间大于等于小于的数量和并和与,然后各种判断。

#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pc __builtin_popcount
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10,mod=998244353,inf=1e9;
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,a[N],Q,st[3][35][N][2],c[N],s[3][N];
bool ans[N];
struct QU{int l,r;}q[N];
inline void init(int w){
    for(int i=1;i<=n;++i){
        s[0][i]=s[0][i-1]+(c[i]<w);s[1][i]=s[1][i-1]+(c[i]==w);s[2][i]=s[2][i-1]+(c[i]>w);
        for(int j=0;j<3;++j)st[j][0][i][0]=INT_MAX,st[j][0][i][1]=0;
        int cur=(c[i]==w?1:(c[i]<w?0:2));
        st[cur][0][i][0]=st[cur][0][i][1]=a[i];
    }
    for(int i=0;i<3;i++)
        for(int j=1;(1<<j)<=n;j++)
            for(int k=1;k+(1<<j)-1<=n;k++){
                st[i][j][k][0]=st[i][j-1][k][0]&st[i][j-1][k+(1<<j-1)][0];
                st[i][j][k][1]=st[i][j-1][k][1]|st[i][j-1][k+(1<<j-1)][1];
            }
}
inline int query(int t,int l,int r,int op){
    int k=std::__lg(r-l+1);int lq=st[t][k][l][op],rq=st[t][k][r-(1<<k)+1][op];
    return op?(lq|rq):(lq&rq);
}
signed main(){
    freopen("peace.in","r",stdin);freopen("peace.out","w",stdout);
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    n=read(),Q=read();for(int i=1;i<=n;++i)a[i]=read(),c[i]=pc(a[i]);
    for(int i=1;i<=Q;++i)q[i].l=read(),q[i].r=read();
    for(int w=0;w<=30;++w){
        init(w);
        for(int i=1;i<=Q;++i){
            int l=q[i].l,r=q[i].r;
            if(l==r)continue;
            if(s[1][r]==s[1][l-1]){
                if(s[0][r]==s[0][l-1])continue;
                if(s[2][r]==s[2][l-1])continue;
                ans[i]|=(query(0,l,r,1)==query(2,l,r,0));
                continue;
            }// 没有相等的,直接判
            int val=query(1,l,r,0);
            if(val!=query(1,l,r,1))continue;// 处理相等的不同情况
            int vl=query(0,l,r,1),vr=query(2,l,r,0);
            if((vl|val)!=val||(vr&val)!=val)continue;// 判断与相等的子集补集关系
            if((vl!=val||s[0][l-1]==s[0][r])+(vr!=val||s[2][l-1]==s[2][r])<=s[1][r]-s[1][l-1])ans[i]=1;//看相等的能否分配
        }
    }
    for(int i=1;i<=Q;i++)puts(ans[i]?"YES":"NO");
}

C 摆烂合唱

建表达式树,然后设 \(f_{i,0/1}\) 表示 \(i\) 节点左/右儿子取值不同对 \(i\) 节点造成影响的概率,\(g_i\) 表示 \(i\) 节点为 \(1\) 的概率,转移就是:

  • 如果当前符号为 &,则这个儿子的概率等于另一个儿子为 \(1\) 的概率,这个点为 \(1\) 的概率等于两个儿子都为 \(1\) 的概率。
  • 如果当前符号为 ^,则这个儿子的概率为 \(1\),这个点为 \(1\) 的概率直接算。
  • 如果当前符号为 |,也相应算即可。

最后每个点到根的乘积就是答案。

D 对称旅行者

妙妙题,首先转成期望的形式,最后再乘上方案数就是答案,\(f_i\) 表示 \(i\) 的期望位置,有 \(f_i=\frac{2f_{i-1}-f_i+2f_{i+1}-f_i}{2}=f_{i-1}+f_{i+1}-f_i\),啥意思,就是每次移动相当于关于 \(i-1\)\(i+1\) 的中点做对称。
然后人类智慧地设出一个 \(g_i=f_{i+1}-f_i\),如果知道了最终的 \(g_i\),因为 \(f_1\) 已知,就能得出所有的 \(f_i\),考虑每次移动对 \(g_i\) 的影响,上面说是在做对称,所以移动一次相当于交换 \(g_i\)\(g_{i-1}\),然后这就是一个置换形式了,直接类似快速幂做这个置换就做完了。

#include<bits/stdc++.h>
#define int __int128
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10,mod=1e9+7,inf=1e9;
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,m,K,g[N],p[N],x[N],ans[N],zc[N],f[N];
inline int qpow(int a,int b){int res=1;for(;b;b>>=1,a=a*a%mod)if(b&1)res=res*a%mod;return res;}
inline void huan(int *a,int *b){
	for(int i=1;i<n;++i)zc[i]=a[b[i]];
	for(int i=1;i<n;++i)a[i]=zc[i];
}
inline void QP(){
	for(int i=1;i<n;++i)p[i]=i,ans[i]=i;
	for(int i=1;i<=m;++i)std::swap(p[x[i]],p[x[i]-1]);
	for(;K;K>>=1,huan(p,p))if(K&1)huan(ans,p);
	for(int i=1;i<n;++i)p[i]=ans[i];
	for(int i=1;i<n;++i)ans[i]=g[p[i]];
}
signed main(){
    freopen("travel.in","r",stdin);freopen("travel.out","w",stdout);
    std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
    n=read();for(int i=1;i<=n;++i)f[i]=read();
    for(int i=1;i<n;++i)g[i]=f[i+1]-f[i];
    m=read(),K=read();for(int i=1;i<=m;++i)x[i]=read();int mul=qpow(2,m*K);QP();
	for(int i=2;i<=n;++i)f[i]=f[i-1]+ans[i-1];for(int i=1;i<=n;++i)std::cout<<(ll)(((f[i]*mul)%mod+mod)%mod)<<' ';
}

总结

妈的,T1 想到正解忘了再连一条边,T2 感觉不该不会,T3 不会就是纯傻逼,T4 不会暴力就是纯傻逼。

posted @ 2024-11-20 17:15  Ishar-zdl  阅读(29)  评论(4编辑  收藏  举报