"蔚来杯"2022牛客暑期多校训练营1 GADICJ (对官方题姐的一些理解,新手向)(未完)

"蔚来杯"2022牛客暑期多校训练营1

https://ac.nowcoder.com/acm/contest/33186#question

我队战绩:3/11(其实是队友战绩。。因为我太菜了)

Problem A. Villages: Landlines

题意

整晕了。。反正就是给n个区间,然后求把所有区间连在一起的代价

解法

也就是看没有被区间覆盖到的长度是多少即可

Code

主要是题目太长吓死人了

#include <bits/stdc++.h>
#define int long long

using namespace std;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int n;
int l[N], r[N];

signed main () {
    cin >> n;
    for (int i = 0; i < n; i ++) {
        int x, y;
        cin >> x >> y;
        l[i] = x - y, r[i] = x + y;
    }

    sort (l, l + n), sort (r, r + n);
    int ans = 0;
    for (int i = 1; i < n; i ++) {
        if (l[i] > r[i-1])   ans += l[i] - r[i-1];
    }
    cout << ans << endl;

}

//把所有区间连起来的代价

Problem B. Spirit Circle Observation

Problem C. Grab the Seat!

题意

(结合样例的图来说明)

给定 n,m,k,q, 那么 座位的范围就是1-n(横),1-m(竖),
在y轴上有一段屏幕(范围是1-m)(图上最左边黑的那一块),然后现在有k 个学生已经占了位置(表示为图中的红点),占了位置的学生会挡住后面的位置(也就是图中黄色区域的部分)。然后有 q 次修改(已经占了的位置)。问每次修改过后,没被挡住的位置有多少个(也就是图中绿色的点的个数)。

分析

所以问题就转化为 求解每一次被挡住的并集。

观察上图,不难得出一条性质:每次只需要考虑当行横坐标最小的(被占的)点挡住的区域即可(因为在同一行内,横坐标大的能覆盖的区域 一定会小于 横坐标小的能覆盖的区域,随便找两个点画画图就知道了)

所以就是黑色的段分上下,挨个统计,更新最大的斜率,然后计算该斜率下能被用到的点有多少

至于具体怎么统计这个点,可以找延长线的交点。
即 第i列交到第i+1列的 点的横坐标与第i+1列的第一个点进行比较,取较小的那个

Code

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int N = 2e5 + 5;
int n, m, k, q;
int x[N], y[N], minx[N], ans[N];

void init () {
    for (int i = 1; i <= m; i ++)
        ans[i] = n, minx[i] = n + 1;
    for (int i = 1; i <= k; i ++)
        minx[y[i]] = min (minx[y[i]], x[i]);
}

int count () {
    init ();
    int dx, dy;

    //统计上半部分的斜率
    dx = 1, dy = 0;
    for (int i = 1; i <= m; i ++) {
        //第一列,斜率为0
        if (i == 1) ans[i] = min (ans[i], minx[i] - 1);
        else {
            if ((i-1)*dx > minx[i]*dy)  dx = minx[i], dy = i-1;
            int cnt = ((i-1)*dx + dy-1)/dy - 1;
            ans[i] = min (ans[i], cnt);
        }
    }

    //统计下半部分的斜率
    dx = 1, dy = 0;
    for (int i = m; i >= 1; i --) {
        //第m列,斜率为0
        if (i == m) ans[i] = min (ans[i], minx[i] - 1);
        else {
            if ((m-i)*dx > minx[i]*dy)  dx = minx[i], dy = m-i;
            int cnt = ((m-i)*dx + dy-1)/dy - 1;
            ans[i] = min (ans[i], cnt);
        }
    }

    int sum = 0;
    for (int i = 1; i <= m; i ++)   sum += ans[i];
    return sum;
}

signed main () {
    cin >> n >> m >> k >> q;
    for (int i = 1; i <= k; i ++)
        cin >> x[i] >> y[i];
    while (q --) {
        int id; cin >> id;
        cin >> x[id] >> y[id];
        cout << count () << endl;
    }
}

Problem D. Mocha and Railgun

疯狂都错题之我看不懂字 =_=

题意

给定Q点坐标和半径d(会形成一个小圆),以及一个圆心在原点,半径为r的大圆,求投射在小圆上对应的大圆的弧长
不懂的看这个

Q是会动的!!!

这有一篇十分牛的题姐

Code

#include <bits/stdc++.h>
#define int long long
#define pi 3.1415926535

using namespace std;

void solve () {
    int R, x, y, d;
    cin >> R >> x >> y >> d;
    double base = sqrt (x*x + y*y);
    double cos1 = (base-d) / R, cos2 = (base+d) / R;
    cout << fixed << setprecision (15) << R * (acos(cos1) - acos(cos2)) << endl;
}

signed main () {
    int t;  cin >> t;
    while (t --)    solve ();
}

//求AB对应在圆上半部分的弧长

//Q为AB中点,先求出A,B坐标
//AB 只会包含在圆的里面
//x^2+y^2=r^2
//只能是劣弧

//Q点是会动的啊

Problem E. LTCS

Problem F. Cut

Problem G. Lexicographical Maximum

题意

看半天都看不懂英文。。。
输出 \(1-n\) 中字典序最大的数字

解法

首先9肯定要排在前面(才能满足字典序最大),如果该序列除了最后一位不是全9,那么就可以输出 \(n-1\) 位9。
注意除了最后一位这里,一开始就因为这个WA了,举个例子:

998->998
979->99

所以即使最后一位不是9也有可能字典序最大
(可能还是比较抽象,看代码吧)

Code

#include <bits/stdc++.h>

using namespace std;

int main () {
    string s;
    cin >> s;
    int n = s.size();
    bool suc = true;
    // if (n == 1) {
    //     cout << s << endl;
    //     return 0;
    // }

    for (int i = 0; i < n-1; i ++) {
        if (s[i] != '9') {
            suc = false;
            break;
        }
    }
    if (!suc) {
        for (int i = 0; i < n-1; i ++)    
            cout << '9';
    }
    else    cout << s;
    
}
//1-n中字典序最大的

Problem H. Fly

Problem I. Chiitoitsu

题意

所有的牌有34种,每种牌都有4张
初始牌有 13 张麻将牌,相同牌至多出现 2 张
每轮可以从牌堆摸牌,若达成七对子则赢

若不然则选择手牌中某张牌并丢弃
给定初始手牌,求最优策略下达成七对子的期望轮数

分析

最优策略:如果摸到的牌能和手上的牌凑成对子,则留下,否则丢掉

关于这里如何表示分数,就要用到逆元的知识了

a/b mod p 相当于 a*(pow(a,p-2) mod p)

(1)当a与p互质时,用快速幂求逆元(费马小定理):ksm(a, b, p);
(2)当a与p不互质时,用扩展欧几里得算法求逆元:exgcd(a, p, x, y)。

Code

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int p = 1e9 + 7;
int dp[20][150]; //单排 剩余的排

int ksm(int a, int k) {
    int res = 1;
    while(k){
        if(k & 1) res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}

void pre () {
    for(int i = 3;i <= 123;i ++)
        dp[1][i] = (1 + (((i - 3) * ksm(i,p - 2) % p) * dp[1][i-1] % p)) % p;
    
    for(int i = 3;i <= 13;i += 2){
        for(int j = 3;j <= 123;j ++){
            dp[i][j] = (1 + (((i * 3) * ksm(j,p - 2) % p) * dp[i - 2][j - 1] % p) + (((j - i * 3) * ksm(j,p - 2) % p) * dp[i][j - 1] % p)) % p;
        }
    }
}

void solve () {
    string s;
    cin >> s;
    int s0 = 0;
    map<string, int> mp;
    string t[s.size()];
    for (int i = 0; i < s.size(); i += 2) {
        //string t = "";
        t[i] += s[i];
        t[i] += s[i+1];
        mp[t[i]] ++;
    }

    for (int i = 0; i < s.size(); i += 2) {
        if (mp[t[i]] == 1)  s0 ++;
    }

    cout << dp[s0][123] << endl;
}

signed main () {
    pre();
    int t;
    cin >> t;
    for (int i = 1; i <= t; i ++) {
        cout << "Case #" << i << ": ";
        solve ();
    }
    
}

//策略:
//现有两个的一定不丢
//把单张的丢掉

Problem J. Serval and Essay

题意

分析

Code

Problem K. Villages: Landcircles

posted @ 2022-07-19 23:53  Sakana~  阅读(76)  评论(0编辑  收藏  举报