2026牛客多校第七场题解 ADGKL
场上切了四道题, A 题不会做,看了眼题解补的。
K
模拟题,考虑直接按题意模拟。
L
打表发现,对于任意一个数字 \(b\),它合法的 \(a\) 是以下区间簇。
例:
\([b^2,b^2+b-1]\)
\([b^2+diff,b^2+b-1+diff]\)
\([b^2+2\times diff,b^2+b-1+2*diff]\)
...
其中,\(diff=b*(b+1)\)
那么好做了,算出前面的整段区间,加上最后一个散区间,得到答案。
G
不妨先转化以下题意。
先手方选出两个数 \(u,v\),记 \(X=|u-v|\)。
后手方给 \(X\) 加或不加负号。
最终后手方想要使得\(\sum X_if_i=0\),其中 \(f_i\) 是你施加的符号。
结论是,若出现的数字的种数超过 4 种,那么后手必败。
设这四种数字是 \(a,b,c,d\),且满足 \(a<b<c<d\)。
先手选出 \(a,d\),然后选出 \(b,c\)。
此时若场上的和为 \(d-a+c-b\),先手选出 \(a,b\),然后选出 \(c,d\),这样是没法减到 0 的。
如果此时场上的和为 \(d-a-(c-b)\),这时后手方可以选出 \((a,c)\),\((b,d)\),可以带特定的样例手玩一下。
A
首先需要考虑的性质是,答案可以拆成二进制,一个位置 \(2^k\),如果被选择,只会被至多选择一次。
设我们最终选取的答案是 \(m\)。
那么对于第 \(i\) 个数字,它最小可以被选取的取值是 \(a_i\& (2^{31}-m)\),它的最大可以被选取的值是 \(a_i|m\)。不妨分别将两个端点记作 \(l\) 和 \(r\)。
我们贪心的想,对于第 \(j\) 个二进制位的贡献,如果我发现拿下了剩下的所有 \(j -1\) 个状态,都无法构造出方案,那么当前位置就必须被选取了。
如何检查一个位置是否一定要被选?
对于第 \(1\) 个数字,我肯定会选 \(l_1\),而接下来的每个数字,我们想要贪心的选取 不小于 \(max(l_i,l_{i-1},....l_1)\) 中的最小,可以维护一个这样的前缀来讨论。
经典的,大致是以下的情况:
对于某一段前缀, 被修改后的 \(a_i\) 和 \(lpre\) 是相同的,在某个二进制下,\(a_i\) 允许取 1,但是 \(lpre\) 被取 0,
或 \(a_i\) 和 \(lpre\) 最终相等。
最终这样修改后的 \(a_i\) 若满足:\(l\leq a_i\leq r\),则说明无需添加当前位的贡献。
时间复杂度是 \(O(n\log ^2 A)\)的。
#include<bits/stdc++.h>
// #define int long long
#define se second
#define fi first
#define mt make_tuple
#define pb push_back
using ll=long long;
using namespace std;
typedef tuple<int,int,int>ti3;
typedef pair<int,int>pii;
void Silverwolf(){
int n;
cin>>n;
vector<int>a(n+1,0);
for(int i=1;i<=n;i++){
cin>>a[i];
}
int high=__lg(*max_element(a.begin()+1,a.end()));
int ans=0;
auto check=[&](int bit,int ans)->bool{
int m=ans|((1<<bit)-1);
int l=a[1]&~m;
for(int i=2;i<=n;i++){
int mn=2e9;
int res=0;//累加贡献
l=max(l,a[i]&~m);
int r=a[i]|m;
vector<int>p(high+1,0);
p[0]=((m&1)?0:a[i]&1);
for(int j=1;j<=high;j++)p[j]=p[j-1]+((m>>j&1)?0:(a[i]>>j&1)*(1<<j));
for(int j=high;j>=0;j--){
int u=(l>>j&1);
int v=(a[i]>>j&1);
int ch=(m>>j&1);//是否有机会翻转
if(u==0&&v==0){
if(ch)mn=min(mn,res+(1<<j)+(j>0?p[j-1]:0));
if(j==0)mn=min(mn,res);
}
if(u==0&&v==1){
mn=min(mn,res+(1<<j)+(j>0?p[j-1]:0));
if(!ch)res+=(1<<j);
else res;
if(ch&&j==0){
mn=min(mn,res);
}
}
if(u==1&&v==0){
if(ch){
res+=(1<<j);
if(j==0)mn=min(mn,res);
}else break;
}
if(u==1&&v==1){
res+=(1<<j);
if(j==0)mn=min(mn,res);
}
}
if(mn>=l&&mn<=r){
l=mn;
}else return true;
}
return false;
};
for(int i=high;i>=0;i--){
if(check(i,ans))ans+=(1<<i);
}
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;cin>>T;while(T--)
Silverwolf();
return 0;
}
/*
2
4
2 1 2 1
4
1 2 1 2
output:
2
3
1
6
5 1 3 6 1 6
out:
5
1
11
11 5 15 4 7 14 2 14 11 0 3
o:
13
*/
D
首先发现,如果想要使 \(x\) 留下来,设第一个序列 \(x\) 出现的位置分别是 \(p_1,p_2\),如果第一个序列的 \([1,p_1-1]\) 和第二个序列的 \([1,p_2-1]\) 中出现过除了 \(x\) 的所有数,则答案可能有解。
接下来,则需要判掉首位两个相等但不是 \(x\) 的情况。
只要 \(x\) 没有出现在第一个位置,设开头两个位置是 \(u,v\)
设目前 \(u\) 在第二个序列中出现的位置是 \(pu\),而\(v\) 在第一个序列中出现的位置是 \(p_v\),我们选取 \(p_u,p_v\) 更靠前的那个拿出来直接删掉,可以考虑用树状数组动态维护每个数字出现的位置。
如果 \(x\) 已经出现在了第一个位置,我们始终删另外一个位置的数即可。
(写的比较匆忙,代码好像乱七八糟的)
时间复杂度是 \(O(n\log n)\)的。
#include<bits/stdc++.h>
#define int long long
#define pb push_back
using ll=long long;
using namespace std;
struct Bit{
#define lowbit(x) x&-x
vector<int>c;
int n;
Bit(int n):n(n){
c.assign(n+10,0);
}
int Sum(int x){//查询1~x的前缀和
//查询x的前缀和
int sum=0;
for(int i=x;i>0;i-=lowbit(i)){
sum+=c[i];
}
return sum;
}
int cal(int x,int y){
return Sum(y)-(x?Sum(x-1):0);
}
void add(int x,int y){//将x单点加y
for(int i=x;i<=n;i+=lowbit(i)){
c[i]+=y;
}
}
int find_kth(int k){ // log(n)复杂度
int ans = 0, cnt = 0, i;
for (i = 20; i >= 0; i--)//利用二进制的思想,把答案用一个二进制数来表示
{
ans += (1 << i);
if (ans > n|| cnt + c[ans] >= k)
//这里大于等于k的原因是可能大小为ans的数不在c[ans]的控制范围之内,所以这里求的是 < k
ans -= (1 << i);
else
cnt += c[ans];//cnt用来累加比当前ans小的总组数
}//求出的ans是累加和(即小于等于ans的数的个数)小于k的情况下ans的最大值,所以ans+1就是第k大的数
return ans + 1;
}
# undef lowbit
};
void Silverwolf(){
int n,k;
cin>>n>>k;
vector<int>a(n+1,0),b(n+1,0),p1(n+1,0),p2(n+1,0);
vector<int>del(n+1,0);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++)p1[a[i]]=i;
for(int i=1;i<=n;i++)p2[b[i]]=i;
set<int>st;
for(int i=1;i<=n;i++){
if(a[i]==k)break;
st.insert(a[i]);
}
for(int i=1;i<=n;i++){
if(b[i]==k)break;
st.insert(b[i]);
}
if(a==b)return cout<<"NO\n",void();
if(st.size()==n-1){
// cout<<"YES\n";
vector<int>ans;
int i1=1,i2=1;
Bit tr1(n),tr2(n);
for(int i=1;i<=n;i++){
tr1.add(i,1);
tr2.add(i,1);
}
for(int i=1;i<=n;i++){
while(del[a[i1]])i1++;
while(del[b[i2]])i2++;
if(i==n)break;
if(a[i1]==b[i2])return cout<<"NO\n",void();
// cout<<i<<' '<<i1<<' '<<i2<<'\n';
if(a[i1]==k){
del[b[i2]]=1;
ans.pb(b[i2]);
tr2.add(i2,-1);
tr1.add(p1[b[i2]],-1);
continue;
}
else if(b[i2]==k){
del[a[i1]]=1;
// cout<<a[i1]<<' ';
ans.pb(a[i1]);
tr1.add(i2,-1);
tr2.add(p2[a[i1]],-1);
}
else{
int r1=tr2.Sum(p2[a[i1]]);
int r2=tr1.Sum(p1[b[i2]]);
// cout<<r1<<' '<<r2<<'\n';
if(r1>r2){
del[b[i2]]=1;
ans.pb(b[i2]);
tr2.add(i2,-1);
tr1.add(p1[b[i2]],-1);
}else{
del[a[i1]]=1;
// cout<<a[i1]<<' ';
ans.pb(a[i1]);
tr1.add(i1,-1);
tr2.add(p2[a[i1]],-1);
}
}
}
cout<<"YES\n";
for(auto v:ans)cout<<v<<" ";cout<<'\n';
}else{
cout<<"NO\n";
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// int T;cin>>T;while(T--)
Silverwolf();
return 0;
}
浙公网安备 33010602011771号