当前位置: 代码迷 >> 综合 >> 2021年“图森未来杯”全国程序设计邀请赛 部分题目总结
  详细解决方案

2021年“图森未来杯”全国程序设计邀请赛 部分题目总结

热度:16   发布时间:2023-12-14 05:01:34.0

其中B、F、H、J四题现在看都不太容易,以后有机会再拿出来看看

题目链接
网站链接

目录

  • A. Abstract Algebra
  • C. Countdown
  • D. Divide
  • E. Edge Game
  • G. Group QQ Speed
  • I. I Love You
  • K. K-Primes

A. Abstract Algebra

题目大意:给定两个矩阵A和B,其中
A = [ 1 1 0 1 ] , B = [ 0 1 ? 1 0 ] A= \begin{bmatrix} 1 & 1\\ 0 & 1 \end{bmatrix} , B=\begin{bmatrix} 0&1\\ -1&0 \end{bmatrix} A=[10?11?],B=[0?1?10?]
现在说有一个矩阵
X = [ a b c d ] X= \begin{bmatrix} a & b\\ c & d \end{bmatrix} X=[ac?bd?]
其中, a d ? b c = 1 , a b c d = 0 ad-bc=1,abcd=0 ad?bc=1,abcd=0现在给定整数 a 、 b 、 c 、 d a、b、c、d abcd,保证 X X X能够由若干个 A A A B B B进行矩阵乘积得到,问如何组合,保证有解,输出一组即可,转化成数学语言是 ? 1 0 5 ≤ a i ≤ 1 0 5 -10^5\leq a_i\leq 10^5 ?105ai?105 c i ∈ { A , B } c_i\in \{A,B\} ci?{ A,B} X = ∏ i c i a i X=\prod_ic_i^{a_i} X=i?ciai??

  • 这道题是线性代数矩阵运算的问题,关键在于如何构造,先观察,发现因为 a b c d = 0 , a d ? b c = 0 abcd=0,ad-bc=0 abcd=0,ad?bc=0,假设 b = 0 b=0 b=0,那么有 a d = 1 ad=1 ad=1,因为它们都是整数,所以只能都是1或者都是-1,其他三个也可以类似的考虑,问题一下就变得简单了,这样一个显然的思路是分别讨论为0的时候,但是还有一个关键点,当 c = 0 c=0 c=0的时候(其他也可以类似考虑),
    X = [ 1 b 0 1 ] X= \begin{bmatrix} 1 & b\\ 0 & 1 \end{bmatrix} X=[10?b1?]
    或者
    X = [ ? 1 b 0 ? 1 ] X= \begin{bmatrix} -1 & b\\ 0 & -1 \end{bmatrix} X=[?10?b?1?]
    前者显然有 X = [ 1 b 0 1 ] = [ 1 1 0 1 ] b = A b X=\begin{bmatrix} 1 & b\\ 0 & 1 \end{bmatrix}=\begin{bmatrix} 1 & 1\\ 0 & 1 \end{bmatrix}^b=A^b X=[10?b1?]=[10?11?]b=Ab而后者显然是一个负单位矩阵 × A ? b \times A^{-b} ×A?b,也就是 X = [ ? 1 b 0 ? 1 ] = [ ? 1 0 0 ? 1 ] × A ? b X=\begin{bmatrix} -1 & b\\ 0 & -1 \end{bmatrix}=\begin{bmatrix} -1 & 0\\ 0 & -1 \end{bmatrix}\times A^{-b} X=[?10?b?1?]=[?10?0?1?]×A?b [ ? 1 0 0 ? 1 ] = [ 0 1 ? 1 0 ] 2 = B 2 \begin{bmatrix} -1 & 0\\ 0 & -1 \end{bmatrix}=\begin{bmatrix} 0 & 1\\ -1 & 0 \end{bmatrix}^2=B^2 [?10?0?1?]=[0?1?10?]2=B2所以 X = B 2 × A ? b X=B^2\times A^{-b} X=B2×A?b这样我们就讨论过了 c c c的情况,综上所述 c = 0 c=0 c=0时, X = { A b a = d = 1 B 2 × A ? b a = d = ? 1 X=\begin{cases}A^b &&a=d=1\\B^2\times A^{-b}&&a=d=-1\end{cases} X={ AbB2×A?b??a=d=1a=d=?1?其他 a b d abd abd分别为0时,都可以转化为 c = 0 c=0 c=0的情况
  • 例如 a = 0 a=0 a=0时,可配凑出 X = B × [ ? c ? d 0 b ] X=B\times \begin{bmatrix} -c & -d\\ 0 & b \end{bmatrix} X=B×[?c0??db?]
    也可以凑出 X = B ? 1 × [ c d 0 ? b ] X=B^{-1}\times \begin{bmatrix} c & d\\ 0 & -b \end{bmatrix} X=B?1×[c0?d?b?]
    都可以,后面的矩阵就是刚才讨论过的 c = 0 c=0 c=0的情况,代入上面已经得到的结论可以得到答案, b d bd bd同理,全部求出,写成程序如下
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
int main(){
    //freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);int T;int a, b, c, d;cin >> T;while(T--){
    cin >> a >> b >> c >> d;if(c == 0){
    if(a == 1){
    cout << 1 << "\n" << "A" << " " << b << "\n";}else{
    cout << 2 << "\n" << "B " << 2 << "\n";cout << 'A' << " " << -b << "\n";}}else if(b == 0){
    if(d > 0){
    cout << 3 << "\n";cout << "B 1\n";cout << "A " << -c << "\n";cout << "B -1\n";}else{
    cout << 3 << "\n";cout << "B -1\n";cout << "A " << c << "\n";cout << "B -1\n";}}else if(a == 0){
    if(c > 0){
    cout << 2 << "\n";cout << "B -1\n";cout << "A " << d << "\n";}else {
    cout << 3 << "\n";cout << "B -1\n";cout << "B 2\n";cout << "A " << -d << "\n";}}else if(d == 0){
    if(c > 0){
    cout << 2 << "\n";cout << "A " << a << "\n";cout << "B -1\n";}else{
    cout << 3 << "\n";cout << "B 2\n";cout << "A " << -a << "\n";cout << "B -1\n";}}}return 0;
}
  • 细节需要注意

C. Countdown

  • 计算从4月10日到10月18日有多少天,可以手算出是189天,注意别算错了
cout << 189;

D. Divide

给出四个数 l 1 , r 1 , l 2 , r 2 l_1,r_1,l_2,r_2 l1?,r1?,l2?,r2? ∏ i = l 1 r 1 i \prod_{i=l_1}^{r1}i i=l1?r1?i是不是 ∏ i = l 2 r 2 i \prod_{i=l_2}^{r2}i i=l2?r2?i的约数

  • 根据唯一分解定理,很容易想到的思路是这两个数一定能写成若干质数相乘的形式,如果前面的所有因子集合是后面的子集,那么就是,否则就不是。根据这个思路,枚举每个可能是因子的质数逐一对照,找到一个不合法就说明不是约数
  • 问题等价于 ( l 2 ? 1 ) ! × r 1 ! ∣ ( l 1 ? 1 ) ! × r 2 ! (l_2-1)!\times r_1!|(l_1-1)!\times r_2! (l2??1)!×r1?!(l1??1)!×r2?!,找这四个阶乘的所有因子数对比即可
  • 这道题知识点有两个,素数筛法和求取 n ! n! n!的因子 p p p的个数的方法
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e7 + 100;
const double eps = 1e-6;
int Data[MAXN];
int vis[MAXN];
int p[MAXN];
int Prime(){
    int tot = 0;for(int i=2;i<=10000000;i++){
    if(!vis[i]) p[tot++] = i;for(int j=0;j<tot&&p[j]*i<=10000000;j++){
    vis[i*p[j]] = 1;if(i % p[j] == 0) break;}}return tot;
}
int divide(int n, int p){
    int ans = 0;while(n){
    ans += n / p;n /= p;}return ans;
}
int main(){
    //freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);int T;int a, b, c, d;cin >> T;int tot = Prime();while(T--){
    cin >> a >> b >> c >> d;bool flag = true;for(int i=0;i<tot;i++){
    int num1 = divide(a - 1, p[i]);int num2 = divide(b, p[i]);int num3 = divide(c - 1, p[i]);int num4 = divide(d, p[i]);if(num1-num2+num4-num3 < 0){
    flag = false;break;} }if(flag) cout << "Yes\n";else cout << "No\n";}return 0;
}

E. Edge Game

两人博弈,给出两人起点,图上路径,1号玩家先走,谁先碰到对方谁就赢了,问1号玩家是否能赢

  • 显然是最段路,找两个玩家的最短距离如果是奇数就能赢,否则输,或者用 d f s dfs dfs L C A LCA LCA都一样
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const ll INF = 2147483647;
const int MAXN = 2e6+100;
int head[MAXN];
int vis[MAXN];
int cnt;
ll dis[MAXN];
struct T{
    int id;int dis;bool operator < (const T &a) const{
    return a.dis < dis;}
};
struct Edge{
    int next;int to;ll value;
}edge[MAXN];
void init(int n){
    memset(head,-1,sizeof head);for(int i=0;i<=n;i++) dis[i] = INF;cnt = 0;
}
void Add_Edge(int u, int v, ll value){
    edge[cnt].to = v;edge[cnt].value = value;edge[cnt].next = head[u];head[u] = cnt++;
}
void dijstra(int s){
    priority_queue<T> q;dis[s] = 0;T now;now.dis = dis[s];now.id = s;q.push(now);while(!q.empty()){
    T u = q.top();q.pop();if(vis[u.id]) continue;vis[u.id] = 1;for(int i=head[u.id];i!=-1;i=edge[i].next){
    if(!vis[edge[i].to] && dis[edge[i].to] > edge[i].value + dis[u.id]){
    dis[edge[i].to] = edge[i].value + dis[u.id];now.id = edge[i].to;now.dis = dis[edge[i].to];q.push(now);}}}
}
int main(){
    int T, u, v;scanf("%d", &T);init(T);for(int i=1;i<T;i++){
    scanf("%d%d", &u, &v);Add_Edge(u, v, 1);Add_Edge(v, u, 1);}scanf("%d%d", &u, &v);dijstra(u);if(dis[v] & 1){
    printf("Yes");}else printf("No");return 0;
}

G. Group QQ Speed

题意需要仔细理解, n n n个人玩 Q Q QQ QQ飞车,有 x x x张地图,首先这 n n n个人先 b a n ban ban地图,每人只能 b a n ban ban一个,之后分成 m m m组,平均分组,分到同一组的人必须玩一个地图,但是不能玩同一组的人已经 b a n ban ban了的地图,问最少需要提供多少地图才能保证游戏正常进行

  • 仔细分析一下题意,要注意 b a n ban ban操作是在分组之前进行的,也就是如果分组之后某一组所有地图都已经被 b a n ban ban,这一组本身就不可能被分到一起,如果不得不分,那就说明地图给少了,所以问题问的是最少需要多少地图
  • 很容易想到的是如果 n n n个人分成 n n n组,那么显然最少需要两个地图,如果n个人分成一组,那么至少需要 n + 1 n+1 n+1张地图
  • 关键在于如果不是这两种情况怎么办,考虑样例2,如果两张地图,假如四个玩家分别 b a n ban ban了1,1,1,2,那么就无法分组了,需要留一张地图以备不时之需,所以提供三张地图即可, b a n ban ban同一张地图的人把他们分到同一组,总能分成指定的组数
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
int main(){
    //freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);int t, n, m;cin >> t;while(t--){
    cin >> n >> m;if(n == m) cout << 2;else if(m == 1) cout << n + 1;else cout << 3;cout << "\n";}return 0;
}

I. I Love You

给出两个字符串,问第二个字符串是否是第一个字符串的子序列

  • 用一个 f o r for for循环找一圈即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
bool check(string s, string t){
    int a = s.length();int b = t.length();int num = 0;for(int i=0;i<a;i++){
    if(s[i] == t[num]) num++;if(num == b) break;}if(num == b) return true;return false;
}
int main(){
    //freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);string s, t;cin >> s >> t;int a = s.length();int b = t.length();if(check(s, t)) cout << "Yes";else cout << "No";return 0;
}

K. K-Primes

给出 l l l k k k,问 [ l , l + 2 k ) [l,l+2k) [l,l+2k)区间内是不是 > k \gt k >k个质数

  • 可以发现这个区间段内有 2 k 2k 2k个数,连续的 2 k 2k 2k个正整数里面有多于 k k k个质数,这几乎是不可能的事情,因为连续的 2 k 2k 2k个正整数里面必然有 k k k个偶数,除了 2 2 2之外其他偶数都是合数,所以在 2 2 2这里进行分析特判即可
  • 因为质数越往后约稀疏,可以写几个数发现 l = 2 l=2 l=2时候, k = 1 , 2 , 3 k=1,2,3 k=1,2,3都合法,其他都不行
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int main(){
    //freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);ll l, k;cin >> l >> k;if(l != 2) cout << "No";else{
    if(k < 4) cout << "Yes";else cout << "No";}return 0;
}
  相关解决方案