CF Round 1011 题解合集

here.

挺有意思的一场,但是我真不会 F2。

D

考虑把这个过程反过来,从后往前做,题意转化为每次进行两次操作中的一个:

  • \(r \to r+1\)

  • \(r \to r-k,ans\to ans+d_i\)

你发现这是一个经典的反悔贪心模型。

考虑用小根堆维护目前选择的 \(d_i\),然后每次能选就选,不能选考虑用之前的最小值尝试替换。

复杂度 \(O(n \log n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,k,ans,cnt,a[2000005];
priority_queue<int,vector<int>,greater<int> > q;
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>k;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=n;i>=1;i--){
            if(cnt<k){
                if(q.size() && q.top()<a[i]){
                    q.pop();
                    q.push(a[i]);
                }
                cnt++;
            }else{
                cnt-=k;
                q.push(a[i]);
            }
        }
        ans=cnt=0;
        while(q.size()){
            ans+=q.top();
            q.pop();
        }
        cout<<ans<<'\n';
    }
    return 0;
}

E

纯种诈骗题不是。

考虑两个序列的和做差,记为 \(d\),一定是 \(k\) 的倍数。

然后枚举 \(d\) 的所有因数,暴力检查它们是不是合法的 \(k\)

注意 \(d=0\) 时,因为 \(0\) 是所有数的倍数,所以我们直接检查 \(10^9\) 是否合法就行。

复杂度 \(O(能过)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e9;
int t,n,a[200005],b[200005],c[200005],suma,sumb,k,ans;
bool check(int k){
    if(k>inf) return false;
    for(int i=1;i<=n;i++){
        c[i]=a[i]%k;
    }
    sort(c+1,c+1+n);
    for(int i=1;i<=n;i++){
        if(b[i]!=c[i]) return false;
    }
    return true;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            suma+=a[i];
        }
        for(int i=1;i<=n;i++){
            cin>>b[i];
            sumb+=b[i];
        }
        sort(b+1,b+1+n);
        k=suma-sumb;
        if(k<0){
            cout<<-1<<'\n';
        }else{
            if(!k && check(inf)) ans=inf;
            for(int i=1;i*i<=k;i++){
                if(k%i==0){
                    if(check(k/i)){
                        ans=k/i;
                        break;
                    }else if(check(i)){
                        ans=i;
                        break;
                    }
                } 
            }
            if(ans) cout<<ans<<'\n';
            else cout<<-1<<'\n';
        }
        suma=sumb=k=ans=0;
    }
    return 0;
}

F1

考虑这个移动的过程,相当于在序列中每个颜色选择一个位置,然后让它们向中间靠拢,形成一段连续的区间。

考虑枚举区间和区间中点,我们希望每个颜色选择距离中点最近的一个,这个选择的过程也可以循环找出。

找出所有位置后,把它们排序,依次匹配 \([l,r]\) 中的点。

然后就做完了,复杂度 \(O(n^2 \log n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18;
int t,n,k,ans,sum,a[3005],pos[3005];
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>k;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        ans=inf;
        for(int l=1,r=k,mid=(l+r)>>1;r<=n;l++,r++,mid++){
            memset(pos,-1,sizeof(pos));
            pos[a[mid]]=mid;
            for(int i=1;i<=n;i++){
                if(mid-i>=1 && pos[a[mid-i]]==-1) pos[a[mid-i]]=mid-i;
                if(mid+i<=n && pos[a[mid+i]]==-1) pos[a[mid+i]]=mid+i;
            }
            sum=0;
            sort(pos+1,pos+1+k);
            for(int i=l;i<=r;i++){
                sum+=abs(pos[i-l+1]-i);
            }
            ans=min(ans,sum);
        }
        cout<<ans<<'\n';
    }
}

F2

考虑上面那个做法实在是太朴素了,能不能加点合并处理的部分。

首先我们把区间中点左边第一个遇到颜色为 \(i\) 的位置记为 \(l_i\),区间右边第一个遇到颜色为 \(i\) 的位置记为 \(r_i\)

那么实际上我们求的是 \(\sum_{i=1}^{k} \min(l_i,r_i)\)

考虑区间中点每次右移一位,\(\min(l_i,r_i)\) 只会 \(+1,-1\) 或者不变,具体地,设颜色 \(i\) 两次相邻出现的位置是 \(a,b\),那么当区间中点在 \(\frac{a+b}{2}\) 左侧时,移动一位增加 \(1\),在 \(\frac{a+b}{2}\) 右侧时,移动一位减少 \(1\),注意判断 \((a+b)\) 是奇数的情况。

然后其实可以差分求出贡献的,然后就做完了,但是实现上细节太多并不是很好调。

复杂度 \(O(n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18;
int t,n,k,s,d,ans,a[400005],lst[400005],sum[400005];
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>k;
        ans=inf;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=1;i<=n;i++){
            lst[a[i]]=sum[i]=0;
        }
        s=d=0;
        for(int i=1;i<=n;i++){
            if(!lst[a[i]]){
                s+=i-1;
            }else{
                int pre=lst[a[i]],mid=(pre+i)>>1;
                if((pre+i)&1) sum[mid]--,sum[mid+1]--;
                else sum[mid]-=2;
            }
            sum[i]+=2;
            lst[a[i]]=i;
        }
        d=-k;
        for(int i=1;i<=n;i++){
            ans=min(ans,s);
            d+=sum[i];
            s+=d;
        }
        for(int i=1;i<=k;i++){
            ans-=abs((k+1)/2-i);
        }
        cout<<ans<<'\n';
    }
    return 0;
}
posted @ 2025-03-28 14:24  _Kenma  阅读(23)  评论(0)    收藏  举报