本补题报告仅供参考,顺序由简到难
贪心或者思维。
直接就想到每一个“A”后面有多少个“R”,那么就可以贡献多少个“AR”。所以,只需要凑出n个“AR”即可。
默认长度为n。
直接命令第一个字母为“A”,显然,后面如果全是“R”,只有n-1个“AR”,所以,只需要在倒数第三的位置填上一个“A”即可。
如此,第一个“A”贡献n-2个“AR”,倒数第三位置上的“A”贡献2个“AR”,凑成n个。
并且,通过思考,或者手画可以知道在n <= 3时是无法凑成n个“AR”的,输出-1.
#include
#include
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
if(n <= 3)
cout << -1 << endl;
else
{
cout << "A";
for(int i = 1; i <= n-4; i++)
{
cout << "R";
}
cout << "ARR" << endl;
}
return 0;
}
长度为n,只含有n-1个不同的数字,所以必定有两个数字相同。
组合数学,从n个数字当中选出n-1个数字进行组合,为n。
其次,需要从n-1个数字当中选出哪个数字有两个,为n-1
之后将所有的数字进行排列组合,为n!
由于有两个数字相同,所以排列组合的结果需要 除2。
即ans = n*(n-1)* n! / 2 % 1e9+7
#include
#include
using namespace std;
const long long mod = 1e9 + 7;
const int maxn = 1e5 + 50;
long long a[maxn];
int main()
{
long long n;
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
a[0] = 1;
for(int i = 1; i <= 1e5; i++)
{
a[i] = a[i-1] * i % mod;
}
while(cin >> n)
{
long long ans = n*(n-1)/2%mod;
ans *= a[n]%mod;
ans %= mod;
cout << ans << endl;
}
return 0;
}
值得注意的是,除法与取模的运算顺序,取模之后原本的偶数会变成奇数,进而再进行除法会影响结果,造成误差,导致WA。
求最大的lcm,那肯定是所有n个数的lcm最大。
自然想到了利用gcd来求lcm,多次除法与取余会导致误差WA,想到使用逆元也WA了(不理解)。
转而使用唯一分解定理:一个整数,一定可以唯一的分解为质因数相乘的形式。
针对n个数中的每一个数,我们都进行质因数分解,lcm,所以我们只保留每个质因数的最高幂次。
分解完之后,对所有的质因数进行快速幂取模,相乘取模即可。
#include
#include
using namespace std;
const int N = 1e5 + 50;
const long long mod = 1e9 + 9;
int su[N] = {0};
long long zhi[N];
int z = 0;
int ans[N];
int aishai()
{
su[1] = 1;
for(int i = 2; i <= 1e5 + 5; i++)
{
if(su[i] == 0)
{
zhi[z] = i;
z++;
//num[i] = num[i-1] + 1;
for(int j = 2; j * i <= 1e5 + 5; j++)
{
su[i*j] = 1;
}
}
// else
// num[i] = num[i-1];
}
//cout << z << endl;
}
long long hanshu(long long a)
{
int t = 0;
while(t < z)
{
long long temp = zhi[t] * zhi[t];
if(temp > a) break;
int cnt = 0;
while(a % zhi[t] == 0)
{
a /= zhi[t];
cnt++;
}
ans[zhi[t]] = max(ans[zhi[t]],cnt);
t++;
}
if(a > 1) ans[a] = max(1,ans[a]);//保留本身
}
int quickPower(int x, int y)//快速幂加取模
{
long long result = 1; // 定义答案
while (y > 0) // 当指数大于 0 时进行幂运算
{
if (y & 1) // y 和 1 做与运算,相当于对 2 求模
{
result = (result * x) % mod;// 如果 y 为奇数,则结果只乘一个 x
}
x = x * x % mod; // x 乘二次方,下次使用
y = y >> 1; // y 右移一位,相当于除以 2
}
return result % mod; // 返回结果
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
aishai();
int n;
cin >> n;
for(int i = 1; i <= n; i++)
{
long long x;
cin >> x;
hanshu(x);
}
long long sum = 1;
for(int i = 0; i < z; i++)
{
sum = sum * quickPower(zhi[i],ans[zhi[i]]) % mod;
}
cout << sum % mod << endl;
return 0;
}
写完之后一直T,后来发现是唯一分解定理写垮了,还可以进行优化。
即原本的唯一分解定理是
for(int i = 0; i < z && zhi[i] <= a; i++)
{
int cnt = 0;
if(a % zhi[i] == 0)
{
while(a % zhi[i] == 0)
{
a /= zhi[i];
cnt++;
}
}
ans[zhi[i]] = max(ans[zhi[i]],cnt);
}
简单地对所有质数进行遍历,然后判断相除。数据范围为1e5,共有将近1e4个质数,每次进行遍历复杂度过高。
优化一下:
int t = 0;
while(t < z)
{
long long temp = zhi[t] * zhi[t];
if(temp > a) break;
int cnt = 0;
while(a % zhi[t] == 0)
{
a /= zhi[t];
cnt++;
}
ans[zhi[t]] = max(ans[zhi[t]],cnt);
t++;
}
if(a > 1) ans[a] = max(1,ans[a]);//保留本身
枚举1 ~ 根号(a)的质数,判断是否相除,时间复杂度变为 300.
其次,如果a本身为质数,需要对a本身进行特判
if(a > 1) ans[a] = max(1,ans[a]);//保留本身
由此,可以ac本题。
经过我们的交流与ac,本题的题意不太严谨,可以理解为:位置之间存在固定的映射关系,并且映射关系成环(即:映射13次之后会回归原状)
我们即可用洗2遍来推出洗5遍的结果,因为洗13遍之后会恢复原状。
所以:洗5遍和洗18遍相同。我们不能用洗两遍去求出5遍的结果,但是可以求出18遍的结果,由此得解。
#include
#include
using namespace std;
const int maxn = 50;
int a[maxn];
int b[maxn];
int pos[maxn];//地址的映射
//pos[i] = j;从位置i转移到了位置j
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
string start;
while(cin >> start)
{
if(start[0] == 'A') a[1] = 1;
else if(start[0] == 'J') a[1] = 11;
else if(start[0] == 'Q') a[1] = 12;
else if(start[0] == 'K') a[1] = 13;
else if(start == "10") a[1] = 10;
else
{
a[1] = start[0] - '0';
}
//a[i]表示i位置的值
string s;
//输入初始的后序序列
for(int i = 2; i <= 13; i++)
{
cin >> s;
if(s[0] == 'A') a[i] = 1;
else if(s[0] == 'J') a[i] = 11;
else if(s[0] == 'Q') a[i] = 12;
else if(s[0] == 'K') a[i] = 13;
else if(s == "10") a[i] = 10;
else
{
a[i] = s[0] - '0';
}
}
//输入两次变换后的
for(int i = 1; i <= 13; i++)
{
cin >> s;
if(s[0] == 'A')
{
for(int j = 1; j <= 13; j++)
{
if(a[j] == 1)
{
pos[j] = i;
break;
}
}
}
else if(s[0] == 'J')
{
for(int j = 1; j <= 13; j++)
{
if(a[j] == 11)
{
pos[j] = i;
break;
}
}
}
else if(s[0] == 'Q')
{
for(int j = 1; j <= 13; j++)
{
if(a[j] == 12)
{
pos[j] = i;
break;
}
}
}
else if(s[0] == 'K')
{
for(int j = 1; j <= 13; j++)
{
if(a[j] == 13)
{
pos[j] = i;
break;
}
}
}
else if(s == "10")
{
for(int j = 1; j <= 13; j++)
{
if(a[j] == 10)
{
pos[j] = i;
break;
}
}
}
else
{
for(int j = 1; j <= 13; j++)
{
if(a[j] == s[0] - '0')
{
pos[j] = i;
break;
}
}
}
}
//记录位置的映射关系
int num = 9;//需要18次
while(num--)
{
for(int i = 1; i <= 13; i++)
{
b[pos[i]] = a[i];
}
for(int i = 1; i <= 13; i++)
{
a[i] = b[i];
}
}
for(int i = 1; i <= 13; i++)
{
if(a[i] == 1) cout << "A ";
else if(a[i] == 11) cout << "J ";
else if(a[i] == 12) cout << "Q ";
else if(a[i] == 13) cout << "K ";
else cout << a[i] << " ";
}
cout << endl;
}
return 0;
}
注意地址映射关系的细节实现即可。
数据量比较小,使用dfs深搜即可。
期间需要使用状压思想,但是可以避免使用状压dp的解法。
将一个数字看为二进制,每一位的01表示是否使用过某种行进方式,看二进制1的多少来判断使用行进方式的多少。
#include
#include
using namespace std;
const int maxn = 105;
int map[maxn][maxn];
int dx[10] = {0};
int dy[10] = {0};
int n, m, k;
void dfs(int x, int y, int z)
{
if(x == n && y == m) return ;
for(int i = 0; i < k; i++)
{
int nowx = x + dx[i];
int nowy = y + dy[i];
//新的坐标
int nowz = z|(1<<i);
//每一位表示一种方法
int cnt = __builtin_popcount(nowz);
//求nowz当中有多少个1
if(nowx <= n && nowy <= m && cnt > map[nowx][nowy])
{
map[nowx][nowy] = cnt;
// cout << nowx << " " << nowy << " " << cnt << endl;
dfs(nowx, nowy, nowz);
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
cin >> n >> m >> k;
for(int i = 0; i <= n; i++)
{
for(int j = 0; j <= m; j++)
{
map[i][j] = 0;
}
}
for(int i = 0; i < k; i++)
{
dx[i] = 0;
dy[i] = 0;
}
for(int i = 0; i < k; i++)
{
string s;
cin >> s;
for(int j = 0; j < s.length(); j++)
{
if(s[j] == 'U')
{
dy[i]++;
}
else if(s[j] == 'R')
{
dx[i]++;
}
}
//cout << dx[i] << " " << dy[i] << endl;
}
dfs(0,0,0);
cout << map[n][m] << endl;
}
return 0;
}