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\) 后,问题变成了:
构造一个序列,序列一定是
并且每一位加和为 \(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);
}
}

浙公网安备 33010602011771号