boxmoe_header_banner_img

Hello! 欢迎来到DRheEheAM的blog!

加载中

文章导读

Jul. 2nd 每日一题 | P15303 『NFC-OI R1』序列陆


avatar
DRheEheAM_Garylv229憨毛怪 2026-07-03 66

AI Summary

文章介绍了P15303题目的解法:先通过特殊性质得到部分分,再用多次区间查询确定置换端点,最后反转恢复原序列。

P15303 『NFC-OI R1』序列陆

我们首先观察数据范围。

对于前四个点,我们可以直接简单解决,这里不再赘述。

对于特殊性质2,实际上就是将序列的所有 11 移动到了中间,形成了如下图的结构:

我们可以通过简单的二分来找到任意一个 11 的位置,并求出其前后各有多少 11 ,之后输出即可。

这里给一份所有特殊性质的代码(45 pts):

/*---------------------
by DRheEheAM (awa)-----
love hanser forever!---
---------------------*/
#include<bits/stdc++.h>
using namespace std;
#define intc constexpr int
#define intl long long
#define Cios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
void ask (int l,int r) {
    cout<<"? "<<l<<" "<<r<<endl;
}
int n,k,c;
signed main() {
    Cios;
    cin>>n>>k>>c;
    if (k==n) {
        vector <int> _res;
        for (int i=1;i<=n;i++) {
            ask(i,i);
            int r;
            cin>>r;
            _res.push_back(r);
        }
        cout<<"! ";
        for (int r:_res) cout<<r<<" ";
        return 0;
    }
    if (c==1) {
        ask(1,n);
        int rg;
        cin>>rg;
        cout<<"! ";
        for (int i=1;i<=n;i++) {
            if (n-i+1<=rg) cout<<"1 ";
            else cout<<"0 ";
        }
        return 0;
    }
    if (c==2) {
        ask(1,n);
        int tot;
        cin>>tot;
        int nw=tot;
        int l=1,r=n;
        int id=-1;
        while (l<r&&nw==tot) {
            int mid=(l+r)>>1;
            ask(l,mid);
            int tmp;
            cin>>tmp;
            if (tmp==0) l=mid+1;
            else if (tmp==tot) r=mid;
            else id=mid,nw=tmp;
        }
        cout<<"! ";
        for (int i=1;i<=n;i++) {
            if (i<=id) {
                if (id-i+1<=nw) cout<<"1 ";
                else cout<<"0 "; 
            }
            else {
                if (i-id<=tot-nw) cout<<"1 ";
                else cout<<"0 ";
            }
        }
        return 0;
    }
    return 0;
}

解决完特殊性质,我们来考虑正解。

考虑原序列 aa ,形如下:

我们进行第一次询问 ask(1,n) ,求出的答案记作 tottot ,表示整个序列中 11 的总数。

那么我们可以考虑查询 bb 序列中原本 aa 序列中全为 00 的部分现在有多少 11 ,即查询区间 [1,ntot][1,n-tot] ,记作 ss

之后怎么考虑呢?我们看下图:

这是两种不同的情况。我们需要想办法区分。
容易想到,我们可以查询区间 [1,ntots][1,n-tot-s] 。为什么呢?

若所有的左侧的 11 全部紧贴分界线,中间没有 00 的情况下,这个区间总和应该为 00
那我们查询,若为 00 ,则说明是情况 11,反之为情况 22

若为情况 11 ,我们可以算出置换左端点 l=ntots+1l=n-tot-s+1
若为情况 22 ,我们可以算出置换右端点 r=ntot+kr=n-tot+k

固定了端点和情况之后,我们可以使用剩下的 1717 次查询找到另一个端点,之后手动reverse就好了。具体可以看代码。

/*---------------------
by DRheEheAM (awa)-----
love hanser forever!---
---------------------*/
#include<bits/stdc++.h>
using namespace std;
#define intc constexpr int
#define intl long long
#define Cios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
void ask (int l,int r) {
    cout<<"? "<<l<<" "<<r<<endl;
}
int n,k,c;
int a[100005];
signed main() {
    Cios;
    cin>>n>>k>>c;
    if (k==n) {
        vector <int> _res;
        for (int i=1;i<=n;i++) {
            ask(i,i);
            int r;
            cin>>r;
            _res.push_back(r);
        }
        cout<<"! ";
        for (int r:_res) cout<<r<<" ";
        return 0;
    }
    if (c==1) {
        ask(1,n);
        int rg;
        cin>>rg;
        cout<<"! ";
        for (int i=1;i<=n;i++) {
            if (n-i+1<=rg) cout<<"1 ";
            else cout<<"0 ";
        }
        return 0;
    }
    if (c==2) {
        ask(1,n);
        int tot;
        cin>>tot;
        int nw=tot;
        int l=1,r=n;
        int id=-1;
        while (l<r&&nw==tot) {
            int mid=(l+r)>>1;
            ask(l,mid);
            int tmp;
            cin>>tmp;
            if (tmp==0) l=mid+1;
            else if (tmp==tot) r=mid;
            else id=mid,nw=tmp;
        }
        cout<<"! ";
        for (int i=1;i<=n;i++) {
            if (i<=id) {
                if (id-i+1<=nw) cout<<"1 ";
                else cout<<"0 "; 
            }
            else {
                if (i-id<=tot-nw) cout<<"1 ";
                else cout<<"0 ";
            }
        }
        return 0;
    }
    ask(1,n);
    int tot;
    cin>>tot;
    for (int i=1;i<=n;i++) {
        if (n-i+1<=tot) a[i]=1;
        else a[i]=0;
    } 
    int li=n-tot+1;
    ask(1,li-1);
    int k;
    cin>>k;
    if (tot==0||tot==n||k==0) {
        cout<<"! ";
        for (int i=1;i<=n;i++) cout<<a[i]<<" "; 
        return 0;
    }
    int l=-1,r=-1;
    int res1=0;
    if (li-k-1>=1) {
        ask(1,li-k-1);
        cin>>res1;
    }
    if (res1==0) {
        l=li-k;
        int lf=li,rg=n;
        while (lf<=rg) {
            int mid=lf+rg>>1;
            ask(li,mid);
            int tmp;
            cin>>tmp;
            if (mid-li+1-tmp==k) {
                r=mid;
                rg=mid-1;
            }
            else lf=mid+1;
        }
    }
    else {
        r=li+k-1;
        int lf=1,rg=li-1;
        while (lf<=rg) {
            int mid=lf+rg>>1;
            ask(mid,li-1);
            int tmp;
            cin>>tmp;
            if (tmp==k) {
                l=mid;
                lf=mid+1;
            }
            else rg=mid-1;
        }
    }
    reverse(a+l,a+r+1);
    cout<<"! ";
    for (int i=1;i<=n;i++) cout<<a[i]<<" ";
    return 0;
}



评论(0)

查看评论列表

暂无评论


发表评论

DRheEheAM_Gary Blog