多校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
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18537110