A: A-B Problem
【知识点】数学、思维题
A − B = C < = > A = B + C A-B=C <=> A = B + C A−B=C<=>A=B+C
A可能得取值一共有 R − 2 ∗ L + 1 R-2*L+1 R−2∗L+1种
对于这些 A A A的取值, B B B可能的取值有分别有 1 1 1到 R − 2 ∗ L + 1 R-2*L+1 R−2∗L+1种,AB确定,C就确定了
STD
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
typedef long long ll;
inline void solve()
{
ll L, R;
cin >> L >> R;
if(L + L > R) {
cout << 0 << endl;
} else {
//1+2+3+...+R-2*L+1
cout << (R-L*2+1)*(R-L*2+2) / 2 << endl;
}
}
int main()
{
int t;
cin >> t;
while(t--)
solve();
return 0;
}
B: AB Problem
【知识点】思维题,不涉及算法
首先,可以肯定的事:对于一个字符串,如果开头是A
,结尾是B
,无论如何都没法将两头修改成一样的。
然后,对于一个长度为2的字符串,如果它本身不是回文的,也无法将它修改成回文。
接下来,我们考虑其他情况如何判断:
首先,除了开头是A
,结尾是B
,我们必定能用最多一次操作让两头边对称。
两头已经对称的几个位置就不用管,我们从第一对不对称的位置
开始想:
假如是前AA后BA,我们从后面那个B的前一个位置开始一直往前推进,可以让两头的A中间全部包B;
前BA后BB,从前面这个A开始向后推进,可以让两个B中间全变成A;
前AB后AA,我们从后面第一个A的前一个位置开始一直往前推进,可以让两头的A中间全部包B;
前BB后AB,从前面第二个B开始向后推进,可以让两个B中间全变成A;
所以其余情况一定有可行解!
STD
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
typedef long long ll;
int main()
{
int n;
cin >> n;
string s;
cin >> s;
if(s[0] == 'A' && s[n-1] == 'B' || s == "BA")
puts("No");
else puts("Yes");
return 0;
}
C: Equation Recovery
本场签到题
【知识点】暴力
一共就9种情况,打表即可
STD
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
#define all(x) (x).begin(),(x).end()
constexpr ll MOD = 1e9+7;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int s,a,b,c;
cin>>s>>a>>b>>c;
if(a+b+c==s){
cout<<"+"<<" "<<"+"<<"\n";
}else if(a+b-c==s){
cout<<"+"<<" "<<"-"<<"\n";
}else if((a+b)*c==s){
cout<<"+"<<" "<<"*"<<"\n";
}else if(a-b-c==s){
cout<<"-"<<" "<<"-"<<"\n";
}else if(a-b+c==s){
cout<<"-"<<" "<<"+"<<"\n";
}else if((a-b)*c==s){
cout<<"-"<<" "<<"*"<<"\n";
}else if(a*b*c==s){
cout<<"*"<<" "<<"*"<<"\n";
}else if(a*b+c==s){
cout<<"*"<<" "<<"+"<<"\n";
}else if(a*b-c==s){
cout<<"*"<<" "<<"-"<<"\n";
}
return 0;
}
D: 拼数字的果冻
【知识点】DP,完全背包板子题
这道题因为涉及到知识点,所以定位是本场兴趣赛的最难题
d p [ i ] dp[i] dp[i]表示数字总和为 i i i的背包最大能选的数字个数(即位数最大),跑一个完全背包即可
求出位数最大的情况后再倒序找到最大的方案
由于高位要越大越好,所以倒序找路径时需要9~0循环
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define pdd pair<double, double>
typedef long long ll;
int main()
{
int n, m;
cin >> n >> m;
vector<int> a(10);
int p[10] = {0,2,5,5,4,5,6,3,7,6};
for(int i=1; i<=m; i++)
{
int x;
cin >> x;
a[x] = 1;
}
vector<int> dp(n+5, -1);
dp[0] = 0;
for(int i=1; i<=9; i++)
if(a[i]) {
for(int j=p[i]; j<=n; j++)
dp[j] = max(dp[j], dp[j-p[i]] + 1);
}
int t = n;
for(int i=9; i>=1; i--)
if(a[i])
{
while(t >= p[i] && dp[t-p[i]]+1 == dp[t])
{
cout << i ;
t -= p[i];
}
}
return 0;
}
E: Simple Math Problem
【知识点】数学,dp
这道题的task1就是基础的组合数问题,公式 c ( n , a ) ∗ c ( n − a , b ) ∗ c ( n − a − b , c ) c(n,a)*c(n-a,b)*c(n-a-b,c) c(n,a)∗c(n−a,b)∗c(n−a−b,c)
对于task2,使用的知识点是组合数学中的隔板法模型,因为所有的小球都一样,那么使每个盒子的小球个数不小于其编号数,就是先在每个盒子里放 盒子编号 − 1 盒子编号-1 盒子编号−1 个球,最后再在剩余 r e m rem rem个小球内进行插板,也就是有 C ( r e m − 1 , n − 1 ) C(rem-1,n-1) C(rem−1,n−1)种方法
解法1:快速幂,逆元
由于最后得出的数据量很大,所以直接使用 n ! / ( m ! ∗ ( n − m ) ! ) n!/(m!*(n-m)!) n!/(m!∗(n−m)!)最后取模这种方式求组合数是不行的,肯定会爆数据范围,而1e9+7是一个素数,所以我们可以通过求逆的方式化除法为乘法, a / b = a ∗ i n v ( b ) a/b=a*inv(b) a/b=a∗inv(b),根据费马小定理, a p − 1 ≡ 1 ( m o d ( p ) ) {a^{p-1}}\equiv{1(mod (p))} ap−1≡1(mod(p)),所以 i n v ( b ) ( m o d ( p ) ) ≡ b ( p − 2 ) ( m o d ( p ) ) inv(b)(mod(p))\equiv {b^{(p-2)}}(mod(p)) inv(b)(mod(p))≡b(p−2)(mod(p)),所以我们可以通过快速幂的方法防爆+加速+取模求出逆元,并通过递推的方法求出组合数。
STD (改编自 Unk1ndled 的提交)
#include <stdio.h> #define ll long long #define mod 1000000007 ll qpow(ll x, ll n) { ll res = 1; while (n > 0) { if (n & 1) { res = (res * x) % mod; } x = (x * x) % mod; n >>= 1; } return res; } ll qmod(ll n) { return qpow(n, mod - 2); } ll C(ll n, ll k) { if (k > n - k) { k = n - k; } ll res = 1; for (ll i = 0; i < k; ++i) { res = (res * (n - i)) % mod; res = (res * qmod(i + 1)) % mod; } return res; } int main() { ll n, a, b, c, m; scanf("%lld %lld %lld %lld", &n, &a, &b, &c); ll ans1 = (C(n, a) * C(n - a, b) % mod) * C(n - a - b, c) % mod; printf("%lld\n", ans1); scanf("%lld%lld", &m, &n); for (int i = 1; i <= n; i++) m -= i - 1; ll ans2 = C(m - 1, n - 1); printf("%lld\n", ans2); return 0; }
task 2 解法2:dp(这是我没想到的)
由于数据出水了,所以被学长用dp解法破解了orz,不过多一种解题方法也不是什么坏事
c++题解 by fexla
// // Created by admin on 2023/11/26. // #include <bits/stdc++.h> using namespace std; typedef long long ll; const int M = 1e9 + 7; //取模类,可以实现自动取模 int normalize(int x) { return x < 0 ? (x + M) : (x >= M ? x - M : x); } ll pw(ll a, ll b) { a %= M; ll c = 1; while (b) { if(b & 1)c = c * a % M; a = a * a % M; b >>= 1; } return c; } const int Frac_N = 100; ll frac_inv[Frac_N]; struct frac { ll num; frac(int x = 0) : num(normalize(x)) {} frac(ll x) : num(normalize(x % M)) {} frac inv() const { if(num >= Frac_N)return pw(num, M - 2); return frac_inv[num] ? frac_inv[num] : (frac_inv[num] = pw(num, M - 2)); } frac operator-() const { return M - num; } frac operator+(const frac f2) const { return (num + f2.num) % M; } frac operator-(const frac f2) const { return (num - f2.num + M) % M; } frac operator*(const frac f2) const { return (num * f2.num) % M; } frac operator/(const frac f2) const { return num * f2.inv().num % M; } frac &operator+=(const frac f2) { return *this = operator+(f2); } frac &operator-=(const frac f2) { return *this = operator-(f2); } frac &operator*=(const frac f2) { return *this = operator*(f2); } frac &operator/=(const frac f2) { return *this = operator/(f2); } friend std::istream &operator>>(std::istream &is, frac &f) { ll v; is >> v; f = frac(v); return is; } friend std::ostream &operator<<(std::ostream &os, const frac &f) { return os << f.num; } friend frac operator+(ll a, frac b) { return b + a; } friend frac operator-(ll a, frac b) { return frac(a) - b; } friend frac operator*(ll a, frac b) { return b * a; } friend frac operator/(ll a, frac b) { return frac(a) / b; } }; vector<frac> j(200000); void init() { j[0] = 1; for (int i = 1; i < 200000; ++i) { j[i] = j[i - 1] * i; } } frac C(frac a, frac b) { return j[a.num] / j[a.num - b.num] / j[b.num]; } void solve1() { frac n, a, b, c; cin >> n >> a >> b >> c; cout << C(n, a) * C(n - a, b) * C(n - a - b, c) << '\n'; } void solve2() { frac n, m; cin >> m >> n; m -= (1 + n) * n / 2; vector<vector<frac>> dp(n.num, vector<frac>(m.num + 1, 0)); for (int i = 0; i <= m.num; ++i) { dp[0][i] = 1; } for (int i = 1; i < n.num; ++i) { frac sum = 0; for (int k = m.num; k >= 0; --k) { sum += dp[i - 1][k]; dp[i][k] = sum; } } cout << dp[n.num - 1][0]; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout << setprecision(10) << fixed; init(); solve1(); solve2(); return 0; }
F: 懵哔 Automaton
【知识点】字符串,模拟
按照题意模拟即可,可以开一个计数器,到4的时候进入懵哔时间,循环变量直接+2因为在懵哔时间内字符串不会有任何改变,之后将计数器归零即可
STD
#include<bits/stdc++.h> using namespace std; #define int long long #define all(x) (x).begin(),(x).end() #define allp(x,n) (x).begin()+(n),(x).end() using ll=long long; using pii=pair<int,int>; constexpr ll MOD = 1e9+7; signed main(){ ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); int n; cin>>n; int cnt=0; for(int i=1;i<=n;i++){ cout<<"MB"; cnt++; if(cnt==4){ i+=2; cnt=0; } } return 0; }
G: Reflector
【知识点】数学,模拟
很容易模拟出,由于是等分,所以只需要 ( A i + ( A j − A i ) ∗ ( k + 1 ) ) (A_i+(A_j-A_i)*(k+1)) (Ai+(Aj−Ai)∗(k+1))% n n n即可,但是有一个坑点,就是题目并没有说明 A j A_j Aj一定会大于 A i A_i Ai,因此最后的结果有可能是负数,所以你需要对于负数情况再加上 n ∗ k n*k n∗k使其变成对应的正数再% n n n即可,记得开long long
STD
#include<bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define all(x) (x).begin(),(x).end() #define allp(x,n) (x).begin()+(n),(x).end() #define ms(x,y) memset((x),(y),sizeof((x))) using ll = long long; using pii = pair<int,int>; constexpr ll MOD = 1e9+7; signed main(){ ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); ll st,ed,n,k; cin>>n>>st>>ed>>k; if(ed>=st){ cout<<(st+(ed-st)*(k+1))%n; }else{ cout<<(st+(ed-st)*(k+1)+n*k)%n; } return 0; }