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 不会暴力就是纯傻逼。