本補題報告僅供參考,順序由簡到難
C:給你一個數字n,你需要輸出一個字符串,該字符串長度必須小於等於n,並且含有n個“AR”子序列。
貪心或者思維。
直接就想到每一個“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 <iostream>
#include <algorithm>
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;
}
B題:在1-n之間隨機生成長度爲n的整數序列,請問正好含有n-1個不同的整數的方案數,答案mod 1e9+7
長度爲n,只含有n-1個不同的數字,所以必定有兩個數字相同。
組合數學,從n個數字當中選出n-1個數字進行組合,爲n。
其次,需要從n-1個數字當中選出哪個數字有兩個,爲n-1
之後將所有的數字進行排列組合,爲n!
由於有兩個數字相同,所以排列組合的結果需要 除2。
即ans = n*(n-1)* n! / 2 % 1e9+7
#include <iostream>
#include <algorithm>
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。
E:給你n個數,求出最大的lcm。結果取餘1e9+ 9.
求最大的lcm,那肯定是所有n個數的lcm最大。
自然想到了利用gcd來求lcm,多次除法與取餘會導致誤差WA,想到使用逆元也WA了(不理解)。
轉而使用唯一分解定理:一個整數,一定可以唯一的分解爲質因數相乘的形式。
針對n個數中的每一個數,我們都進行質因數分解,lcm,所以我們只保留每個質因數的最高冪次。
分解完之後,對所有的質因數進行快速冪取模,相乘取模即可。
#include <iostream>
#include <algorithm>
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本題。
A:一共有13張撲克牌,A 2 3 4 5 6 7 8 9 10 J Q K,有一個洗牌機器,現在給你初始的撲克牌序列,再給出洗牌兩次之後的序列,求洗牌5次後的序列。
經過我們的交流與ac,本題的題意不太嚴謹,可以理解爲:位置之間存在固定的映射關係,並且映射關係成環(即:映射13次之後會迴歸原狀)
我們即可用洗2遍來推出洗5遍的結果,因爲洗13遍之後會恢復原狀。
所以:洗5遍和洗18遍相同。我們不能用洗兩遍去求出5遍的結果,但是可以求出18遍的結果,由此得解。
#include <iostream>
#include <algorithm>
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;
}
注意地址映射關係的細節實現即可。
D:從座標(0,0)走到(n,m),期間必須使用給出的行進方式,問最多可以使用多少種行進方式。
數據量比較小,使用dfs深搜即可。
期間需要使用狀壓思想,但是可以避免使用狀壓dp的解法。
將一個數字看爲二進制,每一位的01表示是否使用過某種行進方式,看二進制1的多少來判斷使用行進方式的多少。
#include <iostream>
#include <algorithm>
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;
}