ZJCPC2022 第19届 浙江省赛The 19th Zhejiang Provincial Collegiate Programming Contest(CBALGMIF 8题)

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZJCPC2022 第19届 浙江省赛The 19th Zhejiang Provincial Collegiate Programming Contest(CBALGMIF 8题)相关的知识,希望对你有一定的参考价值。

文章目录

补题链接:https://codeforces.com/gym/103687

C.JB Wants to Earn Big Money

题意:

  • 有 n 个人想买一些股票,m 个人想卖一些股票。每个人都会给出一个价格。
  • 系统将确定最终价格 x。对于想买一些股票的人,如果他给出的价格不低于x,他就会加入交易。对于想卖掉部分股份的人,如果他给出的价格不高于x,他就会加入交易。
  • 求可以加入交易的人数。

思路:

  • 扫一遍序列,判断是否满足条件即可。
#include<bits/stdc++.h>
using namespace std;

int main()
    int n, m, x;  cin>>n>>m>>x;
    int res = 0;
    for(int i = 1; i <= n; i++)
        int t;  cin>>t;  res += (t>=x);
    
    for(int i = 1; i <= m; i++)
        int t;  cin>>t;  res += (t<=x);
    
    cout<<res<<"\\n";
    return 0;

B.JB Loves Comma

题意:

  • 给你一个字符串s,让你在每个cjb子串的后面添加一个逗号后输出。

思路:

  • 直接扫一遍, 在每个cjb后面输出,即可。
#include<bits/stdc++.h>
using namespace std;

int main()
    string s;  cin>>s;
    cout<<s[0]<<s[1];
    for(int i = 2; i < s.size(); i++)
        cout<<s[i];
        if(s[i]=='b' && s[i-1]=='j' && s[i-2]=='c')cout<<',';
    
    return 0;


A.JB Loves Math

题意:

  • 给出两个整数a和b,然后你应该选择一个正奇数 x 和一个正偶数 y(不能更改x和y的值)。
  • 你可以在一次操作中让 a 加 x 或让 a 减 y。求将 a 更改为 b 所需的最少操作数。

思路:

  • 题目等价于让a变成b,可以增加奇数,或者减少偶数。显然次数不超过三次,分类讨论即可。
  • a小于b
    (b-a)为奇数,那么1次
    (b-a)/2为偶数,那么2次(b-a)/2
    (b-a)/2为奇数,那么3次。
  • a大于b
    (a-b)为偶数,那么1次
    (a-b)为奇数,那么2次。增加一,再减去(a-b+1)
#include<bits/stdc++.h>
using namespace std;

int main()
    int T;  cin>>T;
    while(T--)
        int a, b;  cin>>a>>b;
        if(a==b)cout<<"0\\n";
        else if(a<b)
            int t = b-a;
            if(t%2==1)cout<<"1\\n";
            else if(t/2%2==1)cout<<"2\\n";
            else cout<<"3\\n";  //1,2,3
        else
            int t = a-b;
            if(t%2==0)cout<<"1\\n";
            else cout<<"2\\n";
        
    
    return 0;

L.Candy Machine

题意:

  • 给出n个整数(1e9),从中选出若干个,满足这些数中严格大于它们的平均值的数的个数最多。
  • 求最多能选出多少个整数。

思路:

  • 假设最终选择的集合的平均数不超过k,为使平均数不超过 k,应将 ≤ k 的数全部选入,然后贪心选择 > k 的部分中最小的若干个数。
  • 因此将 N 个数从小到大排序后,最优解一定是一个前缀。
  • 枚举每个前缀,二分统计严格大于平均数的数字个数。时间复杂度nlogn。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int a[N];

int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n;  cin>>n;
    for(int i = 1; i <= n; i++)cin>>a[i];
    sort(a+1,a+n+1);
    int res = 0;
    double pre = 0, avg;
    for(int i = 1; i <= n; i++)
        pre += a[i];
        avg = pre/i;
        int l = 0, r = i;
        while(l < r)
            int mid = l+r+1>>1;
            if(a[mid]<=avg*1.0)l = mid;
            else r = mid-1;
        
        res = max(res, i-r);
    
    cout<<res<<"\\n";
    return 0;


G.Easy Glide

题意:

  • 给定二维平面上 n 个(1000)滑行点。已知行走速度为V1,每次经过某个滑行点后可以按V2速度滑行 3 秒。
  • 求从 起点S 滑行到 终点T 所需的最少时间。

思路:

  • 建立一张 n + 2 个点的有向图,分别表示 n 个滑行点以及起点 S 和终点 T。
  • 由起点向每个点连单向边,边权为 S 按 V1 走到至该点所需的时间。
  • 由每个滑行点向其它滑行点以及终点连单向边,边权为先按V2 滑行至多 3 秒然后按 V1 行走至目的地所需的时间。
  • 朴素 Dijkstra 求 S 到 T 的最短路。
  • 复杂度O(n^2)
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>PII;
#define x first
#define y second
const int N = 1010;

int n, v1, v2;
PII a[N];
double e[N][N];

double get_dist(PII x, PII y) return hypot(x.x-y.x, x.y-y.y)/v1; ;
double get_dist2(PII x, PII y)
    double d1 = hypot(x.x-y.x, x.y-y.y);
    return d1<=v2*3? d1/v2 : 3+(d1-v2*3)/v1;


double dist[N];
int vis[N];
double dijkstra()
    memset(dist, 0x42, sizeof dist);
    dist[0] = 0;
    for(int i = 1; i <= n; i++)
        int t = -1;
        for(int j = 0; j <= n; j++)
            if(!vis[j] && (t==-1 || dist[t]>dist[j]))t = j;
        
        vis[t] = 1;
        for(int j = 1; j <= n; j++)
            dist[j] = min(dist[j], dist[t]+e[t][j]);
        
    
    return dist[n];


void solve()
    cin>>n;
    for(int i = 1; i <= n; i++)cin>>a[i].x>>a[i].y;
    n++;
    cin>>a[0].x>>a[0].y>>a[n].x>>a[n].y;
    cin>>v1>>v2;
    memset(e, 0x42, sizeof(e));
    for(int i = 1; i <= n; i++)
        e[0][i] = get_dist(a[0], a[i]);
    
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(i==j)continue;
            e[i][j] = get_dist2(a[i], a[j]);
        
    
    cout<<fixed << setprecision(12) << dijkstra()<<"\\n";


int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T = 1;  //cin>>T;
    while(T--)
        solve();
    
    return 0;

M.BpbBppbpBB

题意:

  • 给定使用两种印章无重叠可旋转地打印出的字符画,统计每种印章的使用次数。

思路:

题解做法:

  • C型的黑格子数为146,S型的黑格子数为100。
  • 假设两种印章分别使用了 x 个和 y 个。
    则黑格子数为146 x + 100 y, 洞数为2 x + y, 联立解方程即可。
  • 考虑如何统计洞数,从上往下、从左往右扫一遍字符矩阵,遇到白格子时做一遍DFS,统计每个连通块中白格子的个数,若为12,检查该白格子的附近是否满足洞的特征。
  • 更方便的做法,直接找出所有的洞的个数。对于两个洞,如果中间相差的距离为7,那么就是C型(因为S拼起来肯定是>7的),那么剩下的就是S啦。
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;

int n, m; 
char a[N][N];

int dx[] = 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3;
int dy[] = 0, 1, -1, 0, 1, 2, -1, 0, 1, 2, 0, 1;
int check(int x, int y)
    if(x-1 < 1 || x+4 > n || y-2 < 1 || y+3 > m)return 0;
    for(int i = 0; i < 12; i++)
        int nx = x+dx[i], ny = y+dy[i];
        if(a[nx][ny] != '.')return 0;
    
    int res = 0;
    for(int i = x-1; i <= x+4; i++)
        for(int j = y-2; j <= y+3; j++)
            if(a[i][j]=='.')res++;
        
    
    return res==12;


void solve()
    cin>>n>>m;
    for(int i = 1; i <= n; i++)cin>>a[i]+1;
    //找出所有洞
    vector<pair<int,int>>vc;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            if(a[i][j] == '.')
                if(check(i,j))
                    vc.push_back(i,j);
                
            
        
    
    //两两判断哪些是C
    int res = 0;
    for(int i = 0; i < vc.size(); i++)
        for(int j = i+1; j < vc.size(); j++)
            int x1 = vc[i].first, y1 = vc[i].second;
            int x2 = vc[j].first, y2 = vc[j].second;
            if((abs(x1-x2)==7 && y1 == y2) || (abs(y1-y2) == 7 && x1 == x2))res++;
        
    
    cout<<res<<" "<<((int)vc.size()-2*res)<<"\\n";


int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T = 1;  //cin>>T;
    while(T--)
        solve();
    
    return 0;

I.Barbecue

题意:

  • 给定一个长度为 n 的字符串 S,q 次询问,每次询问指定 S 的一个子串,两个人在该子串上进行博弈。
  • 博弈双方轮流删去当前串开头或结尾的一个字符,碰到回文串的人输。
    预测两人都按最优策略操作时最终谁会获胜。
  • n, q < 1e6

思路:

  • 首先通过 Hash 或马拉车 等方式 O(1) 特判起始串为回文串的情况。
  • 对于接下来任意一个局面,先手操作前一定不是回文串。若先手无法进行任何操作,则说明无论删去开头还是结尾都会得到回文串。
  • 容易发现满足条件的串只能形如 ab, abab, ababab, . . .这说明终止态的长度一定是偶数,因此输赢只和起始串长度的奇偶性有关。
  • 时间复杂度 O(n + q)。
//马拉车
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,q,l,r;
string s;

int main()
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n,q;
    string s;
    cin>>n>>q>>s;
    vector<int> d1(n);
    for(int i = 0, l = 0,r = -1; i < n; i++)
        int k = (i > r) ? 1 : min(d1[l + r - i],r - i + 1);
        while(0 <= i - k && i + k < n && s[i - k] == s[i + k]) k++;
        d1[i] = k--;
        if(i + k > r)
            l = i - k;
            r = i + k;
        
    
    while(q--)
        cin>>l>>r;
        l--,r--;
        int mid = (l + r)/2;
        以上是关于ZJCPC2022 第19届 浙江省赛The 19th Zhejiang Provincial Collegiate Programming Contest(CBALGMIF 8题)的主要内容,如果未能解决你的问题,请参考以下文章