A. K-divisible Sum
题意:
给出两个整数n和k,要你构造一个由 n 个正整數 a1,a2,…,an 组成的阵列,使 (a1+a2+⋯+an) 之和被 k 整除,且 a 中的最大元素是最小的。输出a中的最大可能元素是多少
思路:
为了使构成的数组的最大值最小,只需要让数组中的元素尽可能相同即让数字都等于n / k,当n / k无法整除时,那么最大值就是 n / k向上取整(ceil)。但是,当n>k时便无法满足,这个时候我们需要让k倍增,使k刚好大于等于n,最终输出答案即可。要注意的是用朴素得for循环倍增会超时
AC代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
long long t,a,b;
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&a,&b);
if(a > b)
{
if(a%b==0) b = a;
else//k倍增
{
long long k = a/b;
b = b*(k+1);
}
}
if(b%a==0) printf("%lld\n",b/a);
if(b%a!=0) printf("%lld\n",b/a+1);
}
return 0;
}
B. Inflation
题意:
给你一个长度为 n 的数列和一个 k ,可以对数列中每一个数加上一个值,要求每个数与其左侧的数之和的比小于等于 k %,输出加上值总和的最小值
思路:
如果某一个点不符合要求,只需要考虑前面最少要加多少即可,不需要考虑加的位置,因为可以都加在 p0 位置。要注意的是在做判断时尽量用乘法代替除法,以减少不必要的精度问题
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int INF = 0x3f3f3f3f;
long long p[maxn];
long long pre[maxn];
int main()
{
int t,n,k;
cin >> t;
while(t--)
{
cin >> n >> k;
memset(pre,0,sizeof(pre));
for(int i = 1;i <= n;i++)
{
cin >> p[i];
pre[i+1] = pre[i]+p[i];
}
long long ans = 0;
for(int i = n;i > 1;i--)
{
if(p[i]*100 > pre[i]*k)
{
long long d = p[i]*100/k-pre[i];
if((pre[i]+d)*k<p[i]*100) d+=1;
ans += d;
}
pre[i-1] += ans;
}
cout << ans << endl;
}
return 0;
}
C. Longest Simple Cycle
题意:
有 n 条链,ai 表示第 i 条链的第一个点与前一条链所连接的点的编号,bi 表示第 i 条链的最后一个点与前一条链所连接的点的编号,ci 表示第 i 条链的长度(共有几个节点)。求节点数最多的环,并输出其节点数。
思路:
从最左边的链遍历,可以遇到的环有两种
1.连接下一链的两节点为同一点,那一点代表当前环结束
2.连接下一链的两节点为不同点,那两点之间的边为环最后的边
我们只需要对每一条链上的两个节点进行判断,再利用贪心算法就能得到有最多边的环
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
int a[maxn],b[maxn],c[maxn];
int main()
{
int t,n;
cin >> t;
while(t--)
{
cin >> n;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
for(int i = 1;i <= n;i++) cin >> c[i];
for(int i = 1;i <= n;i++) cin >> a[i];
for(int i = 1;i <= n;i++) cin >> b[i];
for(int i = 1;i <= n;i++) if(a[i]>b[i]) swap(a[i],b[i]);//保证上节点为较小的点
long long sum = b[2]-a[2];//第一条链
long long ans = 0;
for(int i = 2;i<=n;i++)
{
sum+=2;//链接下一条链的两边
if(a[i+1]==b[i+1])//出现了第一种环
{
ans = max(ans,sum+c[i]-1);
sum = 0;
continue;
}
ans = max(ans,sum+c[i]-1);
if(b[i+1]-a[i+1]>sum+c[i]-1-(b[i+1]-a[i+1]))sum=b[i+1]-a[i+1];//出现了第二种环
else sum+=c[i]-1-(b[i+1]-a[i+1]);
ans = max(ans,sum);
}
cout << ans << endl;
}
return 0;
}
D. Journey
题意:
有n+1个城市,编号从0到n,n条道路连接这些城市,第i条道路连接城市i-1和i(i∈[1,n])。
每条道路都有一个方向。如果第i个字符是L,则表示第i条路最初是从城市i到城市i-1,否则是从城市i-1到城市i。道路的方向每天变换一次。
每天,旅行者必须使用其中一条道路从他们目前所在的城市到邻近的城市。
计算如果旅行者从城市i开始,在一次旅行中最多可以访问多少个不同的城市。
思路:
因为每天道路都会变换一次方向,所以只有下一条路与当前路方向不同我们才有可能一直走下去,并且每次回到原点时所有路的方向和初始值一样
所以对于每个点,所能到达的范围就是一直往左走加上一直往右走的最远距离
预处理出相邻不同的前缀 f 和后缀 g 就可以
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
char s[N];
int T, n, f[N], g[N];
int main()
{
cin >> T;
while (T--)
{
cin >> n;
scanf("%s", s + 1);
f[1] = 1;//前缀和
for (int i = 2;i <= n;i++) f[i] = (s[i] == s[i - 1]) ? 1 : f[i - 1] + 1;
g[n] = 1;//后缀和
for (int i = n - 1;i >= 1;i--) g[i] = (s[i] == s[i + 1]) ? 1 : g[i + 1] + 1;
for (int i = 1;i <= n + 1;i++)
{
int ans = 1;
if (i - 1 >= 1 and s[i - 1] == 'L') ans += f[i - 1];//向右走
if (i <= n and s[i] == 'R') ans += g[i];//向左走
printf("%d%c", ans, " \n"[i == n + 1]);
}
}
return 0;
}