CF1928题解

CF1928A

首先我们想要拼出新的矩形,必然要把横向拼接的切割开,变成纵向拼接(横向纵向不固定)

所以只能对半切割,新的矩形为(a/2,b),所以a必须是偶数边(a,b不固定),此外,如果a/2=b那么拼接完的矩形和原矩形大小相等,也不可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
bool check(int a,int b){
    if(!(a%2)&&(a/2!=b))  return 1;
    return 0;
}
int main(){
    int n,a,b;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a,&b);
        if(check(a,b)||check(b,a))  printf("yes\n");
        else  printf("no\n");
    }
}

CF1928B

靠,这道题竟然没做出来

我先想到的是贪心,但是好像没法确定需要变成的相同元素,二分也不具有单调性

其实是推性质题目

性质一:发现相等的值因为排列不相同,所以只会贡献一次

性质二:一段去重后数列中的数,最大值和最小值差<n,则数列中的所有数都可以变为同一个数

去重后双指针即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int t,n,ans;
int a[N];
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        sort(a+1,a+1+n);
        int len=unique(a+1,a+1+n)-a-1;
        ans=0;
        for(int l=1,r=1;l<=len;l++){
            while(r<len&&a[r+1]-a[l]<n){
                r++;
            }
            ans=max(ans,r-l+1);
        }
        printf("%d\n",ans);
    }
}

CF1928C

周期为 \(2*k-2\)

然后将x推到x=1的位置有两种情况

\(n-g\)\(n+g-2\)

他们等于 \(m*(2*k-2)\)

然后就将以上两种枚举因数p

p要满足2|p,并且k>=x,用map去重

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int t,n,x,ans;
map<int,bool>mp;
void check(int g,int x){
    int len=sqrt(g)+1;
    for(int i=1;i<=len;i++){
        if(g%i==0){
            if(!mp[i]&&i%2==0&&(i+2)/2>=x){
                mp[i]=1;
                ans++;
            }
            if(!mp[g/i]&&(g/i)%2==0&&(g/i+2)/2>=x){
                mp[g/i]=1;
                ans++;
            }
        }
    }
}
int main(){
    scanf("%d",&t);
    while(t--){
        mp.clear();
        ans=0;
        scanf("%d%d",&n,&x);
        check(n-x,x);
        check(n+x-2,x);
        printf("%d\n",ans);
    }
}

CF1928D

观察到 \(\sum c_i<=2e5\)

设分队数为 \(g\)

性质1:若 \(g>=c_i\) 那么当前种族的贡献不用考虑

已知 \(g\) ,怎么算 \(c_i\) 的贡献?

贪心的将种族的人平摊分队更优,此部分可以 O(1) 计算

所以 \(g\) 从1枚举到 \(\max c_i\)

然后对于 \(g<c_i\) 的每一位重新计算贡献

总复杂度 \(O(\sum c_i)\)

思路懂了,不想写了

CF1928E

很显然把一个 \(a_i\) 转化为 \(b_i*y+x%y\)

将构造的每一个数都减去 \(x%y\) 后,问题变成了:

构造一个序列,序列一定是

\[\lfloor \frac{x}{y}\rfloor,\lfloor \frac{x}{y}\rfloor+1,\lfloor \frac{x}{y}\rfloor+2,\cdots,k_0,0,1,\cdots,k_1,0,1,\cdots,k_2,\dots,0,0,0,\dots \]

并且每一位加和为 \(s'\) (第一步处理过的s)

先不考虑第一段 \(\lfloor \frac{x}{y}\rfloor,\lfloor \frac{x}{y}\rfloor+1,\lfloor \frac{x}{y}\rfloor+2,\cdots,k_0\)

就是考虑已知序列的和 \(s\) 可不可以在长度为 \(n\) 内构造出这样一个首项为0(因为不考虑第一段)的序列

先考虑贪心选大的,是错的,因为没法保证贪心选大的后留出来的小的更优

观察到每次可以往上连续加一段 \(0,1,\cdots,k_i\) ,所以dp

dp要维护能否在 \(n\) 的长度内构造出和为 \(i\) 的序列

根据dottle大佬讲解的优化方法,考虑到和这一维不好压缩,但长度这一维具有单调性

所以设 \(dp[i]\) 表示和为 \(i\) 时能够造出的序列最短的长度

转移即为 \(dp[i]=\min (dp[i-\sum_{k=0}^j k]+j+1)\)

然后我们考虑一下复杂度

欸?你这里枚举 \(j\) 难道不是 \(n^2\) 的吗

我们考虑到 \(\sum_{k=0}^j k<=s\) 一个等差数列的求和公式为 \(\sum_{k=0}^j k=j*(j+1)/2\) 所以 \(j\) 最多只会被枚举 \(\sqrt s\)

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

考虑怎么样输出,我们模拟dp转移的过程,若从上一个状态能转移到这一个状态,证明转移的过程一定在序列中

具体实现,和需要注意的细节请查看代码

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5,inf=1e9;
int t,x,y,s,n;
int f[N];
void solve(int n,int x,int y,int s){
    if((ll)(x%y)*n>(ll)s){//有可能爆int的导致判断错误
        printf("NO\n");
        return;
    }
    s=s-(x%y)*n;
    if(s<0||s%y){
        printf("NO\n");
        return;
    }  
    s/=y;
    if(s<x/y){
        printf("NO\n");
        return;
    }  
    f[0]=0;
    for(int i=1;i<=s;i++)  f[i]=inf;
    for(int i=1;i<=s;i++){
        int sum=0;
        for(int j=0;;j++){
            sum+=j;
            if(sum>i)  break;
            f[i]=min(f[i],f[i-sum]+j+1);
        }
    }
    int sum=0;
    for(int len=1;len<=n;len++){//这里<=n
        sum+=x/y+len-1;
        if(sum>s){
            printf("NO\n");
            return;
        }
        if(len+f[s-sum]<=n){
            printf("YES\n");
            for(int i=1;i<=len;i++){
                printf("%d ",(x/y+i-1)*y+x%y);
            }
            for(int p=s-sum;p;){
                int val=0;
                for(int j=0;;j++){
                    val+=j;
                    // if(val>p){
                    //     printf("NO!\n");
                    //     return;
                    // }
                    if(f[p]==f[p-val]+j+1){
                        for(int k=0;k<=j;k++){
                            printf("%d ",x%y+k*y);
                        }
                        p-=val;
                        break;
                    }
                }
            }
            for(int i=len+f[s-sum]+1;i<=n;i++){
                printf("%d ",x%y);
            }
            printf("\n");
            return;
        }
    }
    printf("NO\n");//这里也要加
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d%d",&n,&x,&y,&s);
        solve(n,x,y,s);
    }
}

posted @ 2025-08-27 15:57  daydreamer_zcxnb  阅读(68)  评论(0)    收藏  举报