2022“杭电杯”中国大学生算法设计超级联赛(8)题解报告

Problem D. Quel’Thalas

题解:

手玩一下就会发现答案是就是2n。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define pi acos(-1)
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 1e6 + 10;

int n;

void solve()
{
    cin >> n;
    cout << 2 * n << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem A. Theramore

题解:

对于奇偶性相同的位置可以任意交换位置,所以对于奇偶位置单独处理即可。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define pi acos(-1)
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 1e6 + 10;

string s;
char a[N], b[N];

void solve()
{
    cin >> s;
    int len = s.length();
    int la = 0, lb = 0;
    for(int i = 0; i < len; i++){
        if(i % 2) b[++lb] = s[i];
        else a[++la] = s[i];
    }
    sort(a + 1, a + 1 + la);
    sort(b + 1, b + 1 + lb);
    for(int i = 1; i <= len / 2; i ++) cout << a[i] << b[i];
    if(len % 2) cout << b[lb] << '\n';
    else cout << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem K. Stormwind

题解:

枚举小长方形的某个边长的最小值,由此可以求出另一个边长允许的最小值,然后求出两个方向分别最多能画几条线。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define pi acos(-1)
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 1e6 + 10;

int n, m, k;

void solve()
{
    cin >> n >> m >> k;
    int ans = -ans;
    for(int i = 1; i <= n && i <= k; i ++){
        if((k / i + (k % i > 0)) > m) continue;
        ans = max(ans, (n / i) + (m / (k / i + (k % i > 0))) - 2);
    }
    swap(n, m);
    for(int i = 1; i <= n && i <= k; i ++){
        if((k / i + (k % i > 0)) > m) continue;
        ans = max(ans, (n / i) + (m / (k / i + (k % i > 0))) - 2);
    }
    cout << ans << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem E. Ironforge

题解:

求出从每个点出发能到达的最大的范围,即可O(1)回答所有询问。
先从大到小枚举,预处理每个点只向右走的最大范围,如果能向右走一步就先取 i+1 的范围,然后沿着
之前预处理的范围向后跳。向后跳的次数是均摊O(1)的。
然后从小到大枚举,求每个点能走到的最大范围。
如果i能走到i + 1:

        如果i + 1已经预处理的向右的范围无法让它走回i则取预处理的答案;
        否则i + 1和 i 范围相同。
如果 i 不能走到 i+1:
        先取预处理的范围,然后向左向右扩展,都沿着已知的范围暴力跳即可。向左向右跳的次数都是均摊O(1)的。
判断一个范围内是否存在某个质因子,方法有很多,标程采用的是对每个质因子维护一个 vector 存它出现的所有位置,在 vector 上二分。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 2e5 + 10;

int n, m, a[N], b[N], clean[N], tim;
int L[N], R[N];

int pr[N], pcnt, vis[N], low[N];
vector<int> pos[N];

bool Check(int p, int l, int r) {
    if (!pos[p].size() || pos[p].back() < l) return 0;
    int x = *lower_bound(pos[p].begin(), pos[p].end(), l);
    return (x <= r);
}

void init() {
    n = 200000;
    for (int i = 2; i <= n; ++i) {
        if (!vis[i]) {
            pr[++pcnt] = i;
            low[i] = i;
        }
        for (int j = 1; j <= pcnt && i * pr[j] <= n; ++j) {
            vis[i * pr[j]] = 1;
            low[i * pr[j]] = pr[j];
            if (i % pr[j] == 0) break;
        }
    }
}

void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i < n; ++i) cin >> b[i];
    for (int i = 1; i <= n; ++i) {
        int x = a[i];
        while (x > 1) {
            int nowp = low[x];
            while (nowp == low[x]) {
                x /= nowp;
            }
            if (clean[nowp] != tim) {
                pos[nowp].clear();
                clean[nowp] = tim;
            }
            pos[nowp].push_back(i);
        }
    }
    ++tim;
    for (int i = n; i >= 1; --i) {
        R[i] = i;
        while (R[i] < n && Check(b[R[i]], i, R[i])) {
            R[i] = R[R[i] + 1];
        }
    }
    for (int i = 1; i <= n; ++i) {
        if (i > 1 && R[i - 1] >= i) {
            if (Check(b[i - 1], i, R[i])) {
                L[i] = L[i - 1];
                R[i] = R[i - 1];
            } else {
                L[i] = i;
            }
        } else {
            L[i] = i;
            while (1) {
                bool flag = 0;
                while (R[i] < n && Check(b[R[i]], L[i], R[i])) {
                    flag = 1;
                    R[i] = R[R[i] + 1];
                }
                while (L[i] > 1 && Check(b[L[i] - 1], L[i], R[i])) {
                    flag = 1;
                    L[i] = L[L[i] - 1];
                }
                if (!flag) break;
            }
        }
    }
    for (int i = 1, x, y; i <= m; ++i) {
        cin >> x >> y;
        if (L[x] <= y && y <= R[x]) cout << "Yes\n";
        else cout << "No\n";
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    init();
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem G. Darnassus

题解:

依次连接i,i + 1,这样的生成树每条边边权都小于等于n - 1,因此存在一种最小生成树中也
只有边权小于等于n - 1的边(Kruskal 算法的原理)。

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
//#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}

const int N = 5e4 + 5;

int n, pos[N], p[N], rt[N];
int tot, head[N];

struct edg{
    int to, fr, next;
}e[N * 460];

int fin(int a){
    return (rt[a] == a) ? a : rt[a] = fin(rt[a]);
}

void Insert(int u, int v, int w){
    e[++tot].to = v;
    e[tot].fr = u;
    e[tot].next = head[w];
    head[w] = tot;
}

void solve()
{
    n = read();
    for(int i = 1; i <= n;  i++){
        p[i] = read();
        pos[p[i]] = i;
        head[i] = 0;
        rt[i] = i;
    }
    int m = sqrt(n), cnt = n - 1;
    long long ans = 0;
    tot = 0;
    for(int i = 1; i <= n; i++){
        for(int j = i + 1; j <= min(n, i + m); j ++){
            int tmp = (j - i) * abs(p[i] - p[j]);
            if(tmp < n) Insert(i, j, tmp);
            tmp = (j - i) * abs(pos[i] - pos[j]);
            if(tmp < n) Insert(pos[i], pos[j], tmp);
        }
    }
    for(int i = 1; i < n; i++){
        for(int j = head[i]; j; j = e[j].next){
            int u = fin(e[j].fr), v = fin(e[j].to);
            if(u != v){
                rt[u] = v;
                ans += i;
                cnt --;
            }
        }
        if(cnt == 0) break;
    }
    printf("%lld\n", ans);
}

signed main(){
    // ios::sync_with_stdio(0);
    // cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值