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;
}

导航