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

其中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=[1011],B=[0110]
现在说有一个矩阵
X = [ a b c d ] X= \begin{bmatrix} a & b\\ c & d \end{bmatrix} X=[acbd]
其中, a d − b c = 1 , a b c d = 0 ad-bc=1,abcd=0 adbc=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 105ai105 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=iciai

  • 这道题是线性代数矩阵运算的问题,关键在于如何构造,先观察,发现因为 a b c d = 0 , a d − b c = 0 abcd=0,ad-bc=0 abcd=0,adbc=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=[10b1]
    或者
    X = [ − 1 b 0 − 1 ] X= \begin{bmatrix} -1 & b\\ 0 & -1 \end{bmatrix} X=[10b1]
    前者显然有 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=[10b1]=[1011]b=Ab而后者显然是一个负单位矩阵 × A − b \times A^{-b} ×Ab,也就是 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=[10b1]=[1001]×Ab [ − 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 [1001]=[0110]2=B2所以 X = B 2 × A − b X=B^2\times A^{-b} X=B2×Ab这样我们就讨论过了 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×Aba=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×[c0db]
    也可以凑出 X = B − 1 × [ c d 0 − b ] X=B^{-1}\times \begin{bmatrix} c & d\\ 0 & -b \end{bmatrix} X=B1×[c0db]
    都可以,后面的矩阵就是刚才讨论过的 c = 0 c=0 c=0的情况,代入上面已经得到的结论可以得到答案, b d bd bd同理,全部求出,写成程序如下
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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=l1r1i是不是 ∏ i = l 2 r 2 i \prod_{i=l_2}^{r2}i i=l2r2i的约数

  • 根据唯一分解定理,很容易想到的思路是这两个数一定能写成若干质数相乘的形式,如果前面的所有因子集合是后面的子集,那么就是,否则就不是。根据这个思路,枚举每个可能是因子的质数逐一对照,找到一个不合法就说明不是约数
  • 问题等价于 ( l 2 − 1 ) ! × r 1 ! ∣ ( l 1 − 1 ) ! × r 2 ! (l_2-1)!\times r_1!|(l_1-1)!\times r_2! (l21)!×r1!(l11)!×r2!,找这四个阶乘的所有因子数对比即可
  • 这道题知识点有两个,素数筛法和求取 n ! n! n!的因子 p p p的个数的方法
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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;
}

你可能感兴趣的:(#,题目反思与总结)