A | B | C | D | E | F |
---|---|---|---|---|---|
√ | √ | √ | √ | √ | ● |
( √:做出; ●:尝试未做出; ○:已补题 )
题目地址:https://codeforces.com/contest/1355
A Sequence with Digits
题意: a n + 1 = a n + m i n D ( a n ) ∗ m a x D ( a n ) a_{n+1}=a_n+minD(a_n)*maxD(a_n) an+1=an+minD(an)∗maxD(an),其中 minD 表示数位中最小的数,maxD表示数位中最大的数,给定 a 1 a_1 a1 和 K K K,求 a K a_K aK。( 1 ≤ a 1 ≤ 1 0 18 , 1 ≤ K ≤ 1 0 16 1\leq a_1\leq 10^{18},1\leq K\leq 10^{16} 1≤a1≤1018,1≤K≤1016)
思路:数据范围一开始吓了我一跳,不过转念一想,如果 minD 等于0,那么以后的minD都会等于0,所以就一直计算到minD等于0就可以了。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
LL minD(LL x)
{
LL ans=x%10;
x/=10;
while(x)
{
ans=min(ans,x%10);
x/=10;
}
return ans;
}
LL maxD(LL x)
{
LL ans=x%10;
x/=10;
while(x)
{
ans=max(ans,x%10);
x/=10;
}
return ans;
}
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
LL a,k;
while(T--)
{
scanf("%lld%lld",&a,&k);
k--;
while(k--)
{
LL x=minD(a),y=maxD(a);
if(!x) break;
a+=x*y;
}
printf("%lld\n",a);
}
return 0;
}
B Young Explorers
题意:n 个人,每个人有一个属性值 a i a_i ai,现在要给所有人组队(不要求每个人都要入队),要求任意一个人所在的队伍的总人数必须不小于其属性值。问最多能组多少队?
思路:贪心。从属性值小到大枚举,每当选的人数大于等于当前这个人的属性值时新增一队。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=2e5+5;
int n,T,a[maxn];
int main()
{
//freopen("input.txt","r",stdin);
T=read();
while(T--)
{
n=read();
REP(i,1,n) a[i]=read();
sort(a+1,a+n+1);
int ans=0,tot=0,maxx=-1e8;
REP(i,1,n)
{
maxx=max(a[i],maxx);
tot++;
if(tot>=maxx) ans++,tot=0,maxx=-1e8;
}
printf("%d\n",ans);
}
return 0;
}
C Count Triangles
题意:给定四个正整数 A, B, C, D (5e5),然后 x, y, z 的取值要满足 A ≤ x ≤ B ≤ y ≤ C ≤ z ≤ D A\leq x\leq B\leq y\leq C\leq z\leq D A≤x≤B≤y≤C≤z≤D,问有多少种取值,可以构成严格的三角形(不能共线)。
思路:这道题卡了好久……思路很明确,z的长度可以遍历,然后对于每一个z,要在O(1)的时间内计算出取值个数,也就是 x + y > z x+y>z x+y>z 的取值种数。这要分两类,一类是当 y = B y=B y=B的时候 x 可计算,另一类是当 x = B x=B x=B 的时候 y 可计算,然后每一类中分三个小类,也就是当某个等于 B的时候另一个的最小值在另一个所属区间的哪一部分,然后分类计算即可。
官方题解给出了另一种方法,真是太厉害了:只用计算 x + y = z x+y=z x+y=z 的选取种数,然后 x + y > z x+y>z x+y>z 就可以用前缀和。至于计算 x + y = z x+y=z x+y=z 的时候,可以开一个大数组 a[i] 表示 x+y=i 的种数,然后枚举 x 的取值,用差分的思想把 a[i+B,…,i+C] 上所有数 +1,最后两次前缀和,然后枚举 z 计算即可。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int A,B,C,D;
LL cal(int z)
{
if(B+C<=z) return 0;
if(z-B+1<=B)
{
if(z-B+1<=A) return 1ll*(B-A+1)*(C-B+1);
int temp=z-B+1-A+1;
if(temp>=C-B+1)
{
int a1=B-(z-B+1)+1,n=C-B+1;
return 1ll*a1*n+1ll*n*(n-1)/2;
}
else
{
int a1=B-(z-B+1)+1,n=temp;
return 1ll*a1*n+1ll*n*(n-1)/2+1ll*(C-B+1-temp)*(B-A+1);
}
}
else
{
int l=z-B+1;
if(l>C) return 0;
if(C-l+1<=B-A+1) return 1ll*(C-l+1)*(C-l+2)/2;
else
{
int a1=C-l+1,n=B-A+1;
return 1ll*a1*n-1ll*n*(n-1)/2;
}
}
}
int main()
{
//freopen("input.txt","r",stdin);
cin>>A>>B>>C>>D;
LL ans=0;
REP(z,C,D) ans+=cal(z);
cout<<ans;
return 0;
}
D Game With Array
题意:给定 N 和 S,问是否存在一个长度为 N 和为 S 的正整数数列,使得对于某个给定的正整数 K,不能在这个数列中选取某个子集,使得该子集的和等于 K 或 S-K。如果可以给出这个数列的每一项以及 K 的值。
思路:其实这道题我做题的时候是猜的。因为我发现当 S ≥ 2 N S\geq 2N S≥2N 的时候,只要构造数列 { 1 , 1 , 1 , . . . , S − N + 1 } \{1,1,1,...,S-N+1\} {1,1,1,...,S−N+1},然后令 K = S / 2 K=S/2 K=S/2,就可以满足要求;对于 S < 2 N S<2N S<2N 的情况,我试了几个例子,发现都不行,所以就假装这是对的了(实际确实是对的)。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int main()
{
//freopen("input.txt","r",stdin);
int n=read(),s=read();
if(n*2>s) puts("NO");
else
{
puts("YES");
REP(i,1,n-1) printf("1 ");
printf("%d\n",s-n+1);
printf("%d\n",s/2);
}
return 0;
}
E Restorer Distance
题意:初始有 n 根柱子,每根柱子有一个高度 h[i],现在有三种操作:花费 A 使得某根柱子高度+1、花费 R 使得某根柱子高度-1、花费 M 使得某根柱子高度-1而另一根+1。问最少的花费,使得最终所有柱子高度一样。
思路:一般这种正面思考毫无头绪的,那就要考虑一下dp或者模型转换什么的。因为是高度一样所以我考虑过差分,但是这三种操作在差分数组中反而更加复杂了,所以应该不行。后来想到二分,进而想到如果对于某个确定的一致高度 h,是可以算出最少花费的:首先如果 M ≥ A + R M\geq A+R M≥A+R ,那么就不用考虑操作三,因为操作三可以转换为操作一加操作二,那就直接计算就好了;如果 M < A + R M<A+R M<A+R,那么就把所有柱子根据大于小于 h 分类,然后尽可能使用操作三,最后剩下的再用操作一或操作二,也可以很快计算出最小花费。然后我根据样例枚举了所有可能的 h 并计算最小花费,发现花费满足二次函数性质,所以三分的思路就很明显了。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=1e5+5;
int h[maxn],n,a,r,m;
LL cost(int height)
{
int lown=0,highn=0;
LL lows=0,highs=0;
REP(i,1,n)
{
if(h[i]<height) lows+=h[i],lown++;
else if(h[i]>height) highs+=h[i],highn++;
}
LL x=1ll*lown*height-lows;
LL y=highs-1ll*highn*height;
if(m<a+r)
{
if(x<y) return 1ll*x*m+1ll*(y-x)*r;
else return 1ll*y*m+1ll*(x-y)*a;
}
else return x*a+y*r;
}
int main()
{
//freopen("input.txt","r",stdin);
n=read(),a=read(),r=read(),m=read();
REP(i,1,n) h[i]=read();
//REP(i,1,10) cout<<i<<' '<<cost(i)<<endl;
int l=0,r=1e9;
while(l<r-5)
{
int ml=l+(r-l)/3,mr=l+(r-l)*2/3;
if(cost(ml)<cost(mr)) r=mr;
else l=ml;
}
LL ans=1e18;
REP(i,l,r) ans=min(ans,cost(i));
cout<<ans;
return 0;
}
F
题意:
思路:
代码: