蒟蒻CC的补题日记——CF965

40 阅读4分钟

A——Find K Distinct Points with Fixed Center

n为偶数输出 (x1,y1),(x+1,y+1),...,(xn/2,yn/2),(x+n/2,y+n/2)(x-1,y-1),(x+1,y+1),...,(x-n/2,y-n/2),(x+n/2,y+n/2)
n为奇数输出 (x,y),(x1,y1),(x+1,y+1),...,(xn/2,yn/2),(x+n/2,y+n/2)(x,y),(x-1,y-1),(x+1,y+1),...,(x-n/2,y-n/2),(x+n/2,y+n/2)

#include<bits/stdc++.h>
using namespace std;

void solve()
{
    int x,y,k;
    cin>>x>>y>>k;
    if(k%2)
    {
        cout<<x<<' '<<y<<'\n';
        for(int i=1;i<=(k-1)/2;i++)
        {
            cout<<x+i<<' '<<y+i<<'\n';
            cout<<x-i<<' '<<y-i<<'\n';
        }
    }
    else
    {
        for(int i=1;i<=k/2;i++)
        {
            cout<<x+i<<' '<<y+i<<'\n';
            cout<<x-i<<' '<<y-i<<'\n';
        }
    }
}

int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

B——Minimize Equal Sum Subarrays

使原数组的每个数取余n,然后再加1,即pnew[i]=p[i]%m+1pnew[i]=p[i]\%m+1
这样不包含n的每个长度为lenlen的区间都比原数组区间大lenlen,包含n的每个长度为lenlen的区间比原数组区间与原数组相差len1n1|len-1-n-1| (nn减少了n1n-1,除了nn以外每个数比原来大11)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int p[N],q[N];
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>p[i];
    }
    for(int i=1;i<=n;i++)
    {
        q[i]=p[i]%n+1;
        cout<<q[i]<<' ';
    }
    cout<<'\n';
}

int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

C——Perform Operations to Maximize Score

首先先排序,分析可知最大值只能由两种情况更新过来
1.当前位置可加,ans=max1>n(ans,pi+k+midi,0)ans=\max\limits_{1->n}(ans,p_i+k+mid_{i,0})
2.当前位置不可加,ans=max1>n(ans,pi+midi,k)ans=\max\limits_{1->n}(ans,p_i+mid_{i,k})
pip_i表示当前的值,midn,mmid_{n,m}表示去掉第nn个数可增加mm时中位数最大是多少
midn,mmid_{n,m}容易想到使用二分求得,如果数组里面去掉pnp_n后仍存在(n+1)/2(n+1)/2个超过mid的数(包括可增加的数),则返回ture,否则返回false
但是这样的时间复杂度为O(n2log2n)O(n^2log_2n),明显需要优化
观察情况二可以发现
Wi=pi+mini,kW_{i}={p_i+min_{i,k}}
Wi+1=pi+1+mini+1,kW_{i+1}={p_{i+1}+min_{i+1,k}}
Wi+1W_{i+1}pi+mini,kpi+1pi+pi+1pip_i+min_{i,k-p_{i+1}-p_{i}}+p_{i+1}-p_{i}等价(相当于把pip_i变成pi+1p_{i+1}) 轻易可以发现Wi+1W_{i+1}永远大于WiW_{i},故情况二只需要算出Wn=pb+minb,kW_{n}={p_b+min_{b,k}}与情况一取maxmax即可
时间复杂度O(n+nlog2n)O(n+nlog_2n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
pair<ll,int> p[N];
ll a[N];
int b[N];
ll n,k;

bool check(ll u,ll k)
{
    int cnt=0;
    for(int i=n-1;i>=1;i--)
    {
        if(p[i].first>=u) cnt++;
        else if(p[i].second&&p[i].first+k>=u)
        {
            k-=u-p[i].first;
            cnt++;
        }
    }
    if(cnt!=0&&cnt>=(n+1)/2) return 1;
    return 0;
}

void solve()
{
    ll ans=0;
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    p[0]={0,0};
    for(int i=1;i<=n;i++) p[i]={a[i],b[i]};
    sort(p+1,p+1+n);
    int mid=(n+1)/2;

    for(int i=1;i<=n;i++)
    {
        if(p[i].second==1)
        {
            if(n%2==1) 
            {
                if(i<mid) ans=max(ans,p[mid].first+p[i].first+k);
                else ans=max(ans,p[mid-1].first+p[i].first+k);
            }
            else
            {
                if(i<=mid) ans=max(ans,p[mid+1].first+p[i].first+k);
                else ans=max(ans,p[mid].first+p[i].first+k);
            }
        }
    }
    ll l=1,r=1e9+10; 
    while(l<r)
    {
        ll mid=(l+r+1)/2;
        if(check(mid,k)) l=mid;
        else r=mid-1;
    }
    // cout<<l<<'\n';
    // for(int i=1;i<=n;i++) cout<<p[i].first<<' '<<p[i].second<<'\n';

    ans=max(ans,p[n].first+l);
    cout<<ans<<'\n';
}

int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

D——Determine Winning Islands in Race

题意简述:AABB两人比赛谁先到达 nn 点,AA 只能从第 ii 个点到第 i+1i+1 个点, BB 可以在有桥的情况下从aa点直接到 bb 点但是起始点只能在 11 点,两者都不能走对方走过的路,输出当 AA 的起始位置在 1n1-n 时是否能获胜
分析可知当 BB 通过桥到达 AA 的前面时, AA 将永远无法到达 nn 点, AA 会失败
所以当 AA 的位置为 did_i 时,我们需要对每一个 di+1d_i+1did_inn 的点进行判断,找出 BB 到该点所需的步数stpjstp_j ( di+1<=j<=nd_i+1<=j<=n ),如果 jdi<=stpjj-d_i<=stp_j 说明 AABB 更早到达该点, BB 无法通过该点限制 AA 的行动,为了方便比较我们化简一下判断条件,当 di>=jstpjd_i>=j-stp_j 时该点不能阻挡 AA,对于 did_i 如果 di>=jstpjd_i>=j-stp_j ( di+1<=j<=nd_i+1<=j<=n ) 恒成立
di>=MAX(jstpj)(di+1<=j<=n)d_i>=MAX(j-stp_j)(d_i+1<=j<=n)时输出 YESYES,在计算 stpjstp_j 时我发现只有桥的左端点在 did_i 右侧时才有效,在 did_i 左侧,如果该点无法阻挡 AA 那与它相连的点也无法阻挡 AA 如果该点可以阻挡,那与它相连的点也可以阻挡, 并且当桥的右端点在 did_i 左侧时也永远无法阻挡 AA
stpj=min(stpj1+1,stpk+1)(kj形成一个桥)stp_j=min(stp_{j-1}+1,stp_k+1)(k与j形成一个桥)。所以首先按桥的左端点排序,从 1n1-n 遍历,遍遍历边加边减边,用优先队列维护(jstpjj-stp_j)的最大值即可。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=2*N; 
typedef pair<int,int> PII;
int n,m;
PII v[N];
int len[N];
const int inf=0x3f3f3f3f;

void init()
{
    for(int i=1;i<=n;i++) len[i]=i-1;
}

struct cmp{
    bool operator ()(const PII &a,const PII &b)
    {
        if(a.first == b.first) return a.second>b.second;
        return a.first<b.first;
    }
};

void solve()
{
    priority_queue<PII,vector<PII>,cmp> pq;;
    cin>>n>>m;
    init();
    for(int i=1;i<=m;i++)
    {
        int a,b;
        cin>>a>>b;
        v[i]={a,b};
    }
    sort(v+1,v+1+m);
    int d=1;
    for(int i=1;i<n;i++)
    {
        len[i]=min(len[i],len[i-1]+1);
        while(d<=m&&v[d].first<i)
        {
            int a=v[d].first,b=v[d].second;
            len[b]=min(len[b],len[a]+1);
            pq.push({b-len[b],b});
            d++;
        }

        while(pq.size())
        {
            PII it=pq.top();
            if(it.second<=i) pq.pop();
            else break;
        }
        if(pq.size()==0)
        {
            cout<<1;
            continue;
        }
        PII it=pq.top();
        if(i>=it.first) cout<<1;
        else cout<<0;
    }
    cout<<'\n';
}
int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}