题目列表
注:本题解仅供参考
B TAROT Ⅱ (组合数学)
C 可以跟我打一辈子ACM吗 (差分)
D 样例多给一点!题目就好做一点! (模拟)
E 菏泽!(set)
F 巴什博弈 (博弈论)
G 松鼠排序(map/并查集/其他)
H 题面短一点点!让我能真的看见!(数论、快速幂)
A TAROT Ⅰ(臭模拟)
思路:
可以先判断正位和逆位,前78位均为正位,后78位均为逆位;
然后判断当前n所在的区间属于大牌还是小牌,一步步模拟即可。
AC代码:
#include <bits/stdc++.h>
#define int long long
#define no std::cout << "NO\n"
#define yes std::cout << "YES\n"
#define endl std::cout << "\n"
typedef std::pair<int, int> PII;
void solve()
{
int n;
std::cin >> n;
int d;
std::string a, b, c;
if (n <= 78)
c = "Obverse";
else
c = "Reverse", n -= 78;
if (n <= 22)
{
a = "Major";
d = n - 1;
std::cout << a << " " << d << " " << c;
}
else
{
n -= 22;
int f1 = n / 14;
n %= 14;
if (f1 == 0)
a = "Wands";
else if (f1 == 1)
a = "Cups";
else if (f1 == 2)
a = "Swords";
else
a = "Pentacles";
if (n <= 10)
{
d = n;
std::cout << a << " " << d << " " << c;
}
else
{
n -= 10;
if (n == 1)
b = "Squire";
else if (n == 2)
b = "Knight";
else if (n == 3)
b = "Queen";
else
b = "King";
std::cout << a << " " << b << " " << c;
}
}
}
signed main()
{
int t = 1;
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
// std::cin >> t;
while (t--)
solve();
return 0;
}
B TAROT Ⅱ (组合数学)
思路:
不出意外的话,按设定,只需知道组合数的计算公式C(a,b)=a!/((a-b)! (b!)),然后直接进行运算即可。
阶乘的值较大,需要额外处理。
AC代码:
#include <bits/stdc++.h>
#define no std::cout << "NO\n"
#define yes std::cout << "YES\n"
#define endl std::cout << "\n"
typedef std::pair<int, int> PII;
typedef long long LL;
const int mod = 131;
const int N = 1e6 + 10;
int fact[N], infact[N];
int qmi(int a, int k, int p)
{ // 快速幂
int res = 1;
while (k)
{
if (k & 1)
res = (LL)res * a % p;
k >>= 1;
a = (LL)a * a % p;
}
return res;
}
void init2()
{ // 预处理阶乘数组和乘法逆元数组
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i++)
{
fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod; // 用快速幂求乘法逆元
}
}
void solve()
{
int n, m;
std::cin >> n >> m;
init2();
std::cout << 2 * ((LL)fact[n] * infact[m] % mod * infact[n - m] % mod) % mod;
}
int main()
{
int t = 1;
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
// std::cin >> t;
while (t--)
solve();
return 0;
}
C 可以跟我打一辈子ACM吗 (差分、前缀和)
思路
每做出一道x*100的分值的题,实际上就是为(1~x)区间的所有题目的做题数都加上了1。
重复许多次给一段区间的所有数加上某个值的过程,我们就可以用差分来做。
AC代码:
#include <bits/stdc++.h>
#define no std::cout << "NO\n"
#define yes std::cout << "YES\n"
#define endl std::cout << "\n"
typedef std::pair<int, int> PII;
typedef long long LL;
const int mod = 1e6 + 7;
const int N = 1e6 + 10;
LL a[N], s[N];
int i;
void solve()
{
LL n, m;
std::cin >> n >> m;
for (int i = 0; i < n; i++)
{
int x, y;
std::cin >> x >> y;
a[1] += y;
a[x + 1] -= y;
}
for (int i = 1; i <= N; i++)
{
s[i] = std::max(s[i - 1] + a[i], 0LL);
}
while (m--)
{
LL x;
std::cin >> x;
if (x <= N - 10LL)
std::cout << s[x] << "\n";
else
std::cout << 0 << "\n";
}
}
int main()
{
int t = 1;
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
// std::cin >> t;
while (t--)
solve();
return 0;
}
D 样例多给一点!题目就好做一点! (MAP/模拟)
思路:
题中给出了每两个点之间的距离,我们可以预处理出每个点的位置,下面就是简单的加减求和。
AC代码:
#include <bits/stdc++.h>
int main()
{
std::map<std::string, int> m;
m["A"] = 0;
m["B"] = 3;
m["C"] = 4;
m["D"] = 8;
m["E"] = 9;
m["F"] = 14;
m["G"] = 23;
int q;
std::cin >> q;
int ans = 0;
while (q--)
{
std::string a, b;
std::cin >> a >> b;
ans += std::abs(m[a] - m[b]);
}
std::cout << ans << "\n";
return 0;
}
E 菏泽!(set/map/二分)
思路:
如果对于一个ai,如果存在j,使得ai-aj=x或者aj-ai=x,
所以a[i]=a[j]-x或者a[i]=a[j]+x
反过来a[j]=a[i]-x或a[j]=a[i]+x
我们可以把所有的a[j]可能的值用set存起来,看数组里有没有即可。
AC代码:
#include <iostream>
#include <set>
using namespace std;
int n, x;
int a[200010];
set<int> S;
void slove()
{
cin >> n >> x;
bool flag = false;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (!S.count(a[i] - x) || !S.count(a[i] + x))
{
S.insert(a[i] - x);
S.insert(a[i] + x);
}
if (S.count(a[i]))
flag = true;
}
if (!flag)
cout << "No" << endl;
else
cout << "Yes" << endl;
}
int main()
{
slove();
return 0;
}
F 巴什博弈 (博弈论)
思路:
显然,如果 n = m + 1,那么由于一次最多只能取 m 个物品,所以无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,故后者必然取胜。根据这样的规律,我们发现了如何取胜的法则。
如果 n = ( m + 1 ) * r + s ,( r 为任意自然数, 0 ≤ s ≤ m ),那么先取者首先拿走 s个物品,接下来若后取者拿走 k ( 1 ≤ k ≤ m ) 个,那么先取者再拿走 m + 1 − k 个,结果剩下 ( m + 1 ) × ( r − 1 ) 个,以后都保持这样的取法,那么后取者最终会面临 ( m + 1 ) 的局面,而先取者则必然获胜。总之,要保持给对手留下 ( m + 1 )的倍数,最后就一定能获胜。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;cin>>t;
while(t--)
{
int n,m;cin>>n>>m;
if(n%(m+1)==0)cout<<"no"<<endl;
else cout<<"yes"<<endl;
}
return 0;
}
G 松鼠排序(map/并查集/其他)
思路:
首先我们要理解题意,也就是给出n个数,分别是1~n,顺序是乱的,要你选择两个数进行调换,最后变成有序的(从小到大),那么我们可以直接从第一个数开始判断,数a[i]对应的位置一定是i,所以当a[i] !=i时,则对其进行互换,即swap(a[i],a[a[i]]).
这道题同样也有一种置换环的思路,详细请自行查询。
AC代码:
#include <bits/stdc++.h>
int main()
{
int n;
std::cin>>n;
std::map<int,int> m,p;
int res=0;
for(int i=1,x;i<=n;i++){
std::cin>>x;
m[x]=i;//谁是第几位
p[i]=x;//第几位是谁
}
//i去i位,要变成m[i]=i,p[i]=i;
//那么原来在i位的,要去m[i],p[m[i]]
for(int i=1;i<=n;i++){
if(p[i]!=i){
p[m[i]]=p[i];
m[p[i]]=m[i];
p[i]=i;
m[i]=i;
res++;
}
}
std::cout<<res;
return 0;
}
另一种写法
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int a[N];
signed main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int ans=0;
for(int i=1;i<=n;i++)
{
while(a[i]!=i)
{
swap(a[i],a[a[i]]);
ans++;
}
}
cout<<ans;
return 0;
}
H 题面短一点点!让我能真的看见!(数论、快速幂)
思路:
这道题只需要证明一点,即当N为素数时,2^N-1也为素数。
证明如下:
接下来就可以知道P(F(N))就是两个不相同的素数的乘积的N次方的因子个数。
然后根据因子个数公式,就可以得知因子个数为(N+1)的平方。
之后再进行取余和快速幂运算即可。
AC代码:
#include <bits/stdc++.h>
#define int long long
#define no std::cout << "NO\n"
#define yes std::cout << "YES\n"
#define endl std::cout << "\n"
typedef std::pair<int, int> PII;
const int mod = 1e6 + 7;
int qmi(int a, int b, int mod)
{
int ans = 1;
while (b)
{
if (b & 1)
ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void solve()
{
int n;
std::cin >> n;
int p = ((n % mod + 1) % mod) * ((n % mod + 1) % mod)%mod;
int ans = qmi(2LL, p, mod);
std::cout << ans % mod << "\n";
}
signed main()
{
int t = 1;
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
// std::cin >> t;
while (t--)
solve();
return 0;
}