A. Sequence with Digits
思路
- 题意
- 定义:minDigit(x)为x的各个位数中,值最小的那个值
- 定义:maxDigit(x)为各个位数中,值最大的那个值
- 给出一个递推式:
- 在题目中 给我们了t次询问,对于每一次询问给出 的值,让我求第 的值
- 分析:这题先看数据范围 ,运行时间<1,这样的大的数据范围句不是暴力过的一定有规律,,我们考虑:一旦 的某个十进制位上的数字等于0,那么 的乘积就恒等于0,在之后表达式就变成了: 剩下的直接输出就行了
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const int mxn = 2e5 + 10;
int main()
{
/* fre(); */
int T;
scanf("%d", &T);
while(T --)
{
ll n, k;
scanf("%lld %lld", &n, &k);
k -= 1;
while(k --)
{
string s = to_string(n);
int mx = 0, mn = 10;
for(auto x : s)
{
int dig = x - '0';
mx = max(mx, dig);
mn = min(mn, dig);
}
if(mn == 0) break;
n = (n + mx * mn);
}
printf("%lld\n", n);
}
return 0;
}
B. Young Explorers
思路(贪心)
- 题意
- 给我们n个数字,这些数字可以任意组合,形成一组,但是形成的这个组的要求是,这个组能内的 值最大的那个数字的值要小于等于组中数字的数量。这样任意组合最多可以形成几组?(某些数字,可以不必使用它来分组)。
- 分析
- 一个简单的贪心,首先我们考虑要想分的组多,就要尽可能每组分的人少,而还要注意,没组分的人数多少是有值最大的那个人决定的,与有这个策略:我们每次从数字值小到大的顺序,枚举作为组中最大值,这个最大值就是组中需要的人数,例如这个最大值如果是2,那么这个组内要有两人,一个人的值为2,一个人的值要小于等于2(说的不清楚,直接看代码更简单)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const int mxn = 3e5 + 10;
ll ar[mxn];
int main()
{
/* fre(); */
int T;
scanf("%d", &T);
while(T --)
{
int n;
scanf("%d", &n);
memset(ar, 0, (sizeof(ll) + 1) * n );
int t;
for(int i = 1; i <= n; i ++)
{
scanf("%d", &t);
ar[t] ++;
}
ll ans = 0;
ll pre = 0;
for(int i = 1; i <= n; i ++)
{
if(ar[i])
{
ar[i] += pre;
ans += ar[i]/i;
if(i + 1 <= n)
pre = ar[i] - ar[i] / i * i;
}
}
printf("%lld\n", ans);
}
return 0;
}
C. Count Triangles
思路
- 题意
- 给我们四个数 A,B,C,D,存在 ,问x,y,z能够造成的三角方案数是多少种?
- 分析
我先分析一个,能够构成三角形的条件“两边两边之和大于第三边,两边之差小于第三边”,这题的两种方法的思路都是从
枚举第一条边x的取值,这样就相当于x的值是一个定值了,剩下的我们在枚举x的基础上对y属于
这个范围进行讨论,在讨论的时候我们要明白这个样一个恒成立的条件:$y-x<=z$,即:两边之差恒小于第三边
,那我们只需考虑限制第三边的确定就只有一个限制条件:两边之和(x+y)小于第三边(z),下面两种思路都是 :都是对这个限制条件的作的相应解决办法。
- 思路1
- 利用“差分”来解决区间所有元素加上一个相同的值,我们考虑x+y的最小值为a+b,最大值是b+c,我们可以差分计算出,这个区间内每个数有多少种可能的情况,再对数组计算前缀和,最后枚举z,看对于当前的z,有多少x+y>z的组合,输出答案即可。
- 思路2(结合着思路1来理解)
代码1
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI cos(-1)
#define PB push_back
#define ll long long
#define int long long
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 1e6 + 10;
int f[mxn];
signed main()
{
/* fre */
ll a, b, c, d;
scanf("%lld %lld %lld %lld", &a, &b, &c , &d);
for(int i = a; i <= b; i ++)
{
f[i + b - 1] ++;
f[i + c - 1 + 1] --;
}
for(int i = 1; i < mxn; i ++) //求每个位置差分之后的数值
f[i] += f[i - 1];
for(int i = 1; i < mxn; i ++) //求每个位置数值 前缀和
f[i] += f[i - 1];
ll ans = 0;
for(int i = c; i <= d; i ++)
ans += f[mxn - 1] - f[i - 1];
printf("%lld\n", ans);
}
代码2
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI cos(-1)
#define PB push_back
#define ll long long
#define int long long
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
signed main()
{
/* fre */
ll a, b, c, d;
scanf("%lld %lld %lld %lld", &a, &b, &c , &d);
int ans = 0;
for(int i = a; i <= b; i ++)
{
int l = i + b - 1;
int r = i + c - 1; // l <= y <= r 的时候在每个对应的可能的y值为方案数为一个从1开始的公差为1的等差序列:[1, 2, ..., r-l+1]
int s = 0, e = 0, ss = 0, ee = 0;
if(l >= c) s = l - c + 1;
if(r >= c) e = r - c + 1; //[s, e] 之间的数表示答案数(但是有的答案可能超过了又边界,最后要被减去的
if(l > d) ss = l - d; //[ss, ee] 越过d边界的无用边
if(r > d) ee = r - d;
ans += (e - s + 1) * (e + s) / 2; //等差数列求和公式
ans -= (ee - ss + 1) * (ee + ss) / 2;
}
printf("%lld\n", ans);
}
D. Game With Array
思路
- 题意
- 让我们构造一个长度为你n的序列ar,并且序列的和为S,对于这个徐磊ar我们需要找出一个k( ),是ar中任意子串和不等于k或者S-k。
- 分析
- 第一个中思路就是:我们令ar中前n-1个数的值为1,第n个数的值为 , 这样如果 的话,这样我们就可以选择 之间的数作为k,就是yes
- 第二种思路就是:我们令k=1,然后ar中的元素不要出现k或者S-k就行了,只要 ,也即是(n-1)个2和一个 ,这样能构造出来就是yes。
代码(思路1)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<string>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt", "w", stdout); }
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const int mxn = 3e5 + 10;
ll ar[mxn];
int main()
{
/* fre(); */
int n, s;
scanf("%d %d", &n, &s);
int ct1 = n - 1;
int cha = s - (n - 1);
if(ct1 + 1 < cha)
{
printf("YES\n");
for(int i = 1; i <= ct1; i ++)
printf("1 ");
printf("%d\n", cha);
printf("%d\n", ct1 + 1);
}
else
printf("NO\n");
return 0;
}
E. Restorer Distance(三分枚举答案)
思路
- 题意
- 给我们一个序列ar,我们可以进行三种操作: 这三种操作的对应的话费为a,b,c
- 通过使用上面的三种操作,是ar中的元素的值变得相同,需要的最小花费是多少?
- 分析
- 用二分枚举答案,但是这题的最小花费曲线是一个抛物线形状,所以该用三分枚举ar中所有的元素都要的变成的“x”,对于第三个操作 我们令 的意思是如果第三种操作的费用比a+b的值的大的话,我们用第一和第二种操作来代替第三种操作,剩下的就是写一个judge()函数去判断白所有的ar都变成某个数x所需要的费用为judge(x)。
代码
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI cos(-1)
#define PB push_back
#define ll long long
#define int long long
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 1e5 + 10;
int ar[mxn];
int n, a, b, c;
int judge(int level)
{
int sa = 0, sb = 0; //两种操作分别的总费用
for(int i = 1; i <= n; i ++)
{
if(ar[i] < level)
sa += level - ar[i];
if(ar[i] > level)
sb += ar[i] - level;
}
int sc = min(sa, sb);
sa -= sc; sb -= sc;
return sc * c + sa * a + sb * b;
}
signed main()
{
/* fre; */
scanf("%lld %lld %lld %lld", &n, &a, &b, &c);
for(int i = 1; i <= n; i ++)
scanf("%lld", &ar[i]);
c = min(c, a + b);
int l = 1, r = INF;
while(l < r)
{
int lmid = l + (r - l) / 3;
int rmid = r - (r - l) / 3;
if(judge(lmid) < judge(rmid))
r = rmid - 1;
else
l = lmid + 1;
}
printf("%lld\n", judge(l));
}