Acwing周赛分享

Posted 旭风°

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Acwing周赛分享相关的知识,希望对你有一定的参考价值。


title: Acwing周赛28
author: Sun-Wind
date: December 5, 2021

Acwing 周赛28

题面1

给定一个由大写字母构成的字符串 s,请计算其中有多少个子序列 QAQ。
注意,子序列不需要连续。
提示:本题数据范围较小,可以直接三重循环枚举三个字母的位置。
输入格式
一行,一个由大写字母构成的字符串 s。
输出格式
一个整数,表示 s 中子序列 QAQ 的个数。
数据范围
所有测试点满足 1≤|s|≤100。
输入样例1:
QAQAQYSYIOIWIN
输出样例1:
4
输入样例2:
QAQQQZZYNOIWIN
输出样例2:
3

Hint

注意子序列是可以不连续的 比如QAQQ 是有两个QAQ序列的

sol

注意到题面的数据范围是<=100的,也就是可以O(n^3)的解法。
也就是枚举每一个字母的位置,然后判断

代码

#include<iostream>
#include<utility>
#include<string>
using namespace std;
typedef long long ll;
#define fi(i,a,b) for(int i = a; i <= b; ++i)
#define fr(i,a,b) for(int i = a; i >= b; --i)
#define x first
#define y second
#define sz(x) ((int)(x).size())
#define pb push_back
using pii = pair<int,int>;
//#define DEBUG
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
#ifdef DEBUG
    //freopen(D:\\in.txt,r,stdin);
#endif
    string sam;
    cin >> sam;
    int cnt = 0;
    fi(i,0,sz(sam)-1)
        fi(j,i+1,sz(sam)-1)
            fi(k,j+1,sz(sam)-1){
                if(sam[i] == \'Q\' && sam[j] == \'A\' && sam[k] == \'Q\')
                    cnt++;
            }
    cout << cnt << endl;
    return 0;
}

题面2

给定一个长度为 n 的整数序列 a1,a2,…,an。
请你从中选出尽可能多的数。
要求满足如下两个条件之一:
仅选择一个数;
选择至少两个数,且所选择的数的最大公约数大于 1;
输出选出数的最大可能数量。
输入格式
第一行包含整数 n。
第二行包含 n 个整数 a1,a2,…,an。
输出格式
一个整数,表示选出数的最大可能数量。
数据范围
前 6 个测试点满足 1≤n≤10。
所有测试点满足 1≤n≤105,1≤ai≤105。
输入样例1:
3
2 3 4
输出样例1:
2
输入样例2:
5
2 3 4 6 7
输出样例2:
3

Hint

第一个样例选择2和4,最大公约数是2
第二个样例选择2,4,6,最大公约数是2

sol

如果要尽可能地选择多的数,那么肯定存在一个数是他们的最大公约数

1.如果选择的数中有最大的公约数,那么这个数显然是所选择序列中较小的数
我们可以尝试把每个数的质因子给筛出来,然后统计质因子最多的数,这样就是最终的答案
上述我们选择的这个数如果
是一个质数,上述的解法显然成立,可以用反证法证明
是一个合数,那么他肯定是某几个质数的乘积,那么上述的解法仍然正确,可以举例子说明

2.如果选择的数中没有最大的公约数
此情况其实等价于合数的情况

筛质因数的时间复杂度为O(n^1/2)
总的时间复杂度为O(n^3/2)

代码

#include<iostream>
#include<utility>
#include<unordered_map>
using namespace std;
typedef long long ll;
#define fi(i,a,b) for(int i = a; i <= b; ++i)
#define fr(i,a,b) for(int i = a; i >= b; --i)
#define x first
#define y second
#define sz(x) ((int)(x).size())
#define pb push_back
using pii = pair<int,int>;
//#define DEBUG
unordered_map<int,int> mapp;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
#ifdef DEBUG
    //freopen(D:\\in.txt,r,stdin);
#endif
    int n;
    cin >> n;
    fi(i,1,n){
        int x;
        cin >> x;
        fi(j,2,x/j){
            if(x % j == 0){
                mapp[j]++;
                while(x % j == 0){
                    x /= j;
                }
            }
        }
        if(x > 1) mapp[x]++;
    }
    int ans = 0;
    for(auto p:mapp) ans = max(ans,p.y);
    if(ans != 0)
        cout << ans << endl;
    else
        cout << 1 << endl;
    return 0;
}

题面3

有 n 个小朋友,编号 1∼n。
每个小朋友都拿着一个号码牌,初始时,每个小朋友拿的号码牌上的号码都等于其编号。
每个小朋友都有一个幸运数字,第 i 个小朋友的幸运数字为 di。
对于第 i 个小朋友,他可以向第 j 个小朋友发起交换号码牌的请求,当且仅当 |i−j|=di 成立。
注意,请求一旦发出,对方无法拒绝,只能立刻进行交换。
每个小朋友都可以在任意时刻发起任意多次交换请求。
给定一个 1∼n 的排列 a1,a2,…,an。
请问,通过小朋友相互之间交换号码牌,能否使得第 i 个小朋友拿的号码牌上的号码恰好为 ai,对 i∈[1,n] 均成立。
输入格式
第一行包含整数 n。
第二行包含 n 个整数 a1,a2,…,an。
第三行包含 n 个整数 d1,d2,…,dn。
输出格式
共一行,如果能做到,则输出 YES,否则输出 NO。
数据范围
前 6 个测试点满足 1≤n≤10。
所有测试点满足 1≤n≤100,1≤di≤n,保证 a1∼an 是一个 1∼n 的排列。
输入样例1:
5
5 4 3 2 1
1 1 1 1 1
输出样例1:
YES
输入样例2:
7
4 3 5 1 2 7 6
4 6 6 1 6 6 1
输出样例2:
NO
输入样例3:
7
4 2 5 1 3 7 6
4 6 6 1 6 6 1
输出样例3:
YES

Hint

对于第一个样例,刚开始都是1,2,3,4,5
经过一系列交换后显然可以成为5,4,3,2,1
每一个数只能跟他相邻的数交换

sol

刚开始看到数据只有100,想试试能不能用bfs搜素,因为这道题和八数码很像
写了一遍发现只能过将近一半的点
后面写了启发式搜素,虽然速度明显提升了很多,但是还是不能通过全部的点

当然,本题不是八数码那种类型,对于这个序列的每一个位置,只有两种可能去的点,而且每一个点不一定是可达的。如果是可达的,我们对这两个点进行建边,然后用floyd跑一遍,如果两个对应的点是可达的,
就说明对应的数可以通过这一系列交换使得对应的数互换位置,间接地说明其是可达的
算法时间复杂度为O(n^3)
此题有个很有趣的地方就是这个图最后跑出来是g[i][j]
i一定能通过一系列的变换到j,也就是i和j最后会实现交换
并且他们中间路径上的数也可以实现交换
最后的结果是只有i和j交换了,其他路上交换过的数都可以重新交换回来

之前想过一个问题就是,如果g[1][5]存在,那么1和5交换了,但是有可能5那个位置需要的不是1,而是4
那么如果g[5][4]存在
4无论之前通过怎样的交换,在最后都可以换到4这个位置来,然后到5的位置

做个推广,如果一个点到另一个点可达,那么他们路上的点到另一个点也是可达的
这个关系映射到这道题中就是无论4之前换到了任何其他位置,那么他还是可以换回到5这个位置
可以称之为交换的恒定有效性

代码

普通的bfs可以过接近一半的点

#include<iostream>
#include<utility>
#include<string>
#include<unordered_map>
#include<queue>
using namespace std;
typedef long long ll;
#define fi(i,a,b) for(int i = a; i <= b; ++i)
#define fr(i,a,b) for(int i = a; i >= b; --i)
#define x first
#define y second
#define sz(x) ((int)(x).size())
#define pb push_back
using pii = pair<int,int>;
//#define DEBUG
unordered_map<string,int> uor;
const int N = 105;
int d[N];
void bfs(string ss,string mir){
    queue<string> que;
    que.push(ss);
    while(!que.empty()){
        string p = que.front();
        que.pop();
        // cout << p << endl;
        if(p == mir) {cout << "YES" << endl; return;}
        uor[p]++;

        fi(i,0,sz(p) - 1){
            if(i + d[i] < sz(p)){
                string temp = p;
                swap(temp[i],temp[i+d[i]]);
                // cout << temp << endl;
                if(!uor[temp])
                    que.push(temp);
            }
            if(i - d[i] >= 0){
                string temp = p;
                swap(temp[i],temp[i - d[i]]);
                if(!uor[temp])
                    que.push(temp);
            }
        }

    }
    cout << "NO" << endl;
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
#ifdef DEBUG
    //freopen(D:\\in.txt,r,stdin);
#endif
    int n;
    cin >> n;
    string Balloon;
    string mir;

    fi(i,1,n){
        char c;
        cin >> c;
        Balloon.push_back(c);
    }
    fi(i,1,n) mir.push_back((char)(i+48));

    fi(i,0,n-1) cin >> d[i];
    
    bfs(mir,Balloon);
    return 0;
}

启发式搜素可以过一半的点

#include<iostream>
#include<utility>
#include<string>
#include<unordered_map>
#include<queue>
using namespace std;
typedef long long ll;
#define fi(i,a,b) for(int i = a; i <= b; ++i)
#define fr(i,a,b) for(int i = a; i >= b; --i)
#define x first
#define y second
#define sz(x) ((int)(x).size())
#define pb push_back
using pii = pair<int,int>;
//#define DEBUG
unordered_map<string,int> uor;
const int N = 105;
int d[N];
struct node{
    string st;
    int dis;
    bool operator < (const node p) const{
        return dis > p.dis;
    }
};
int Guass(string ss,string mir){
    int ans = 0;
    fi(i,0,sz(ss)-1){
        if(ss[i] != mir[i]) ans++;
    }
    return --ans;
}
priority_queue<node> que;
void bfs(string ss,string mir){
    que.push({ss,Guass(ss,mir)});
    while(!que.empty()){
        node p = que.top();
        que.pop();
        // cout << p << endl;
        if(p.st == mir) {cout << "YES" << endl; return;}
        uor[p.st]++;

        fi(i,0,sz(p.st) - 1){
            if(i + d[i] < sz(p.st)){
                string temp = p.st;
                swap(temp[i],temp[i+d[i]]);
                // cout << temp << endl;
                if(!uor[temp])
                    que.push({temp,Guass(temp,mir)+1+p.dis});
            }
            if(i - d[i] >= 0){
                string temp = p.st;
                swap(temp[i],temp[i - d[i]]);
                if(!uor[temp])
                    que.push({temp,Guass(temp,mir)+1+p.dis});
            }
        }

    }
    cout << "NO" << endl;
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
#ifdef DEBUG
    //freopen(D:\\in.txt,r,stdin);
#endif
    int n;
    cin >> n;
    string Balloon;
    string mir;

    fi(i,1,n){
        char c;
        cin >> c;
        Balloon.push_back(c);
    }
    fi(i,1,n) mir.push_back((char)(i+48));

    fi(i,0,n-1) cin >> d[i];
    
    bfs(mir,Balloon);
    return 0;
}

正解1图论

#include<iostream>
#include<utility>
using namespace std;
typedef long long ll;
#define fi(i,a,b) for(int i = a; i <= b; ++i)
#define fr(i,a,b) for(int i = a; i >= b; --i)
#define x first
#define y second
#define sz(x) ((int)(x).size())
#define pb push_back
using pii = pair<int,int>;
//#define DEBUG
const int N = 105;
int d[N];
int Balloon[N];
int g[N][N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
#ifdef DEBUG
    //freopen(D:\\in.txt,r,stdin);
#endif
    int n;
    cin >> n;
    fi(i,1,n) cin >> Balloon[i];
    fi(i,1,n) cin >> d[i];
    
    fi(i,1,n) fi(j,1,n) g[i][j] = 0x3f3f3f3f;
    fi(i,1,n) g[i][i] = 0;

    fi(i,1,n){
        if(i + d[i] <= n)
            g[i][i+d[i]] = g[i+d[i]][i] = 1;
        if(i - d[i] >= 1)
            g[i][i-d[i]] = g[i-d[i]][i] = 1;
    }

    fi(k,1,n) fi(i,1,n) fi(j,1,n)
        g[i][j] = min(g[i][k] + g[k][j],g[i][j]);
    
    fi(i,1,n){
        if(g[i][Balloon[i]] == 0x3f3f3f3f) {
            cout << "NO" << endl;
            return 0;
        }
    }
    cout << "YES" << endl;
    return 0;
}

正解2并查集
用并查集实现的话相当于一个可达性问题,根据之前图论的思路。
时间复杂度为O(nlogn)

#include<iostream>
#include<utility>
using namespace std;
typedef long long ll;
#define fi(i,a,b) for(int i = a; i <= b; ++i)
#define fr(i,a,b) for(int i = a; i >= b; --i)
#define x first
#define y second
#define sz(x) ((int)(x).size())
#define pb push_back
using pii = pair<int,int>;
//#define DEBUG
const int N = 105;
int Balloon[N];
int d[N];
int f[N];
int find(int x){
    return f[x] == x ? x: f[x] = find(f[x]);
}
void combin(int x,int y){
    f[find(x)] = f[find(y)];
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
#ifdef DEBUG
    //freopen(D:\\in.txt,r,stdin);
#endif
    int n;
    cin >> n;
    fi(i,1,n) cin >> Balloon[i];
    fi(i,1,n) cin >> d[i];
    fi(i,1,n) f[i] = i;
    fi(i,1,n){
        if(i + d[i] <= n) combin(i,i+d[i]);
        if(i - d[i] > 0) combin(i,i-d[i]);
    }
    fi(i,1,n){
        if(find(i) != find(Balloon[i]))
            {
                cout << "NO" << endl;
                return 0;
            }
    }
    cout << "YES" << endl;
    return 0;
}

以上是关于Acwing周赛分享的主要内容,如果未能解决你的问题,请参考以下文章

Acwing第 38 场周赛

acwing周赛时间

acwing周赛啥水平

AcWing第一场周赛

Acwing第 53 场周赛完结

AcWing第23场周赛题解