AtCoder Regular Contest 113 题解
前言
难受,我还是一如既往的菜……
ABC
给定一个正整数\(K\),求出正整数排列\((A,B,C)\),使\(A×B×C ≤ K\)的组合个数。
就,枚举前两个元素 \(i\), \(j\),第三个元素的范围就是 \([1,K / i / j]\), 所以直接加上个数 \(K / i / j\) 就好啦。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
int k,n,ans;
signed main() {
scanf("%lld",&k);
for(int i = 1; i <= k; i ++) {
n = k / i;
for(int j = 1; j <= k / i; j ++) ans += n / j;
}
printf("%lld\n",ans);
return 0;
}
/*
样例输入1
2
样例输入2
10
样例输入3
31415
样例输出1
4
样例输出2
53
样例输出3
1937281
*/
A ^ B ^ C
给定三个数 \(A\), \(B\), \(C\),求出 \(A^{B^C}\)的个位上的数。
妈妈我会做小学奥数题了诶!!1(
应该有其他简单多了的算法,但是我懒。
众所周知,次方的个位数只能在 \([1,9]\) 之间这不是废话吗迟早有一天它会出现循环节的。
所以直接考虑将循环节暴力搞出来并且在计算的时候把循环节记录下来,然后将循环节长度设为模数用快速幂算出 \(B^C\) ,最后直接输出就好了。。
注意如果最后取模余 \(0\), 输出的是循环节最后一位。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e4;
int a,b,c,ans,n,m,tot,tag,lu[N],pos[15];
bool cnt[15], f;
int qpow(int a,int b,int mod) {
int res = 1;
while(b) {
if(b & 1) {
if(!f && res >= tag) f = 1;
res *= a;
if(f) res %= mod;
}
a *= a, b >>= 1;
if(f) a %= mod;
}
return res;
}
signed main() {
scanf("%d %d %d",&a,&b,&c);
a %= 10, n = a, cnt[a] = 1, lu[++tot] = n, pos[a] = 1;
// printf("%d\n",a);
while(1) {
n = n * a % 10;
if(cnt[n]) {
tag = pos[n] - 1, m = tot - pos[n] + 1;
break;
}
lu[++tot] = n, pos[n] = tot, cnt[n] = 1;
}
// printf("%d %d\n",tag,m);
// for(int i = 1; i <= m; i ++) printf("%d ",lu[tag + i]);
// printf("\n");
tot = qpow(b,c,m);
if(!f) {
ans = qpow(a,tot,10);
//printf("%d\n",ans);
}
else {
tot = (tot - tag + m) % m;
if(!tot) tot = m;
printf("%d",lu[tag + tot]);
}
return 0;
}
/*
样例输入1
4 3 2
样例输入2
1 2 3
样例输入3
3141592 6535897 9323846
样例输出1
4
样例输出2
1
样例输出3
2
*/
String Invasion
鉴于题面的描述十分不给力,手玩儿几个样例我们可以清楚的理解,题目要求的是 每次可以从任意一位开始替换,任意一位可以被多次替换 后最终次数的最大值。
再康康那个样例 \(3\) , 你会发现从后往前替换是正确的贪心策略。
因为从后往前,可以保证每一对相临的相同元素都能被找到,且替换它们后面的所有元素。而如果从前往后,第一次替换就直接把后面的替换完了以至于 \(Game\) \(Over\)。。
所以从后往前,先预处理出前缀数量和 \(sum[][27]\),和上一次找到的两个相同元素的字符 \(las\) 和前一个的位置 \(las_pos\) 以及当前找的相同元素的后一位位置 \(now\)
如果 \(las\) 和 \(now\) 字符相同, 对答案产生贡献的只有它们之间的一段, 否则还需要加上 \(las\) 后面的一段。
哎,看代码吧。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2e5 + 5;
#define ll long long
int n,dp[N],las,now,las_pos,sum[N][27];
ll ans;
char s[N];
int main() {
scanf("%s",s + 1);
n = strlen(s + 1), las_pos = n + 1;
for(int i = 1; i <= n; i ++) {
sum[i][s[i] - 'a' + 1] ++;
for(int j = 1; j <= 26; j ++) sum[i][j] += sum[i - 1][j];
}
for(int i = n; i; i --) {
if(s[i] == s[i - 1]) {
now = s[i] - 'a' + 1;
ans += las_pos - 1 - i - sum[las_pos - 1][now] + sum[i][now];
if(now != las) ans += n - las_pos + 1;
las_pos = i - 1, las = now;
}
}
printf("%lld",ans);
return 0;
}
/*
样例输入1
accept
样例输入2
atcoder
样例输入3
anerroroccurred
样例输出1
3
样例输出2
0
样例输出3
16
*/
Sky Reflector
给定\(N\),\(M\),\(K\),\(A_i\) 是第 \(i\) 行的最小值,\(B_j\) 是第 \(j\) 列的最大值,网格中每个数的大小不超过\(K\),求\((A,B)\)的所有可能性(答案对\(998244353\)取模)
嘤,考场脑抽了,没做出来。。。
排列组合加上一丢丢容斥
首先读题,不要去管那些有的没的,我们可以得到性质:
- \(\forall\) \(B_j\) \(\ge\) \(\max\){\(A_i\)}
也就是 \(B_i\) \(\in\) \([A_i,k]\)
所以我们直接枚举 \(A_i\) 的最大值,而 \(B_i\) 的取值又一目了然,排列组合就完了。
但是因为 \(C(i,n)\) 并不能保证序列中有 \(i\), 为了保证存在 \(i\), 聪明的你动动小脑瓜就知道了,
柿子应该改为 :
加上快速幂,完啦~
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int mod = 998244353;
int n,m,k,ans,tmp;
int qpow(int a,int b) {
int res = 1;
while(b) {
if(b & 1) res = res * a % mod;
a = a * a % mod, b >>= 1;
}
return res;
}
signed main() {
scanf("%lld %lld %lld",&n,&m,&k);
if(n > m) swap(n,m);
if(n == 1) {
ans = qpow(k,m);
printf("%lld\n",ans);
return 0;
}
for(int i = 1; i <= k; i ++) {
tmp = (qpow(i,n) - qpow(i - 1,n)) % mod, tmp = (tmp + mod) % mod;
ans = (ans + tmp * qpow(k - i + 1,m) % mod) % mod;
}
printf("%lld",ans);
return 0;
}
/*
样例输入1
2 2 2
样例输入2
1 1 100
样例输入3
31415 92653 58979
样例输出1
7
样例输出2
100
样例输出3
469486242
*/
E
至于剩下的……大家都懂对吧...
我不会(理直气壮┑( ̄Д  ̄)┍