“蔚来杯“2022牛客暑期多校训练营部分题解

已更新一、五、八、九、+、十场

第一场

A

题意

在数轴上由n个发电站,其辐射范围为ri,发电站可以无线连接发电塔,而发电塔之间需要电线连接,不限制发电塔数量,问最少线路连接整个电力系统

解题思路

根据题意转换为区间合并问题,只要将所有区间合并后,累加区间之间的空隙即可

核心代码

const int N = 2e5 + 10;
pii a[N];
vector<pii> res;
int ans;

void solve()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        int x, r;
        cin >> x >> r;
        a[i].first = x - r;
        a[i].second = x + r;
    }     
    sort(a, a + n);
    res.push_back(a[0]);
    for (int i = 1; i < n; ++i)
    {
        if (a[i].first > res.back().second)
        {
            res.push_back(a[i]);
        }
        else
        {
            res.back().second = max(res.back().second, a[i].second);
        }
    }
    for (int i = 1; i < res.size(); ++i)
    {
        ans += res[i].first - res[i - 1].second;
    }
    cout << ans;
}

C

题意

在教室中有一面黑板,开始时由n*m个座位,部分上有人,问有几个座位可以看清黑板全貌。

解题思路

本题属于数学几何题
在这里插入图片描述
画图发现对于下面的点所辐射的范围斜率大的会覆盖到斜率小的,因此在逐行遍历时选择最靠左的即可。

核心代码

const int N = 2e5 + 10;
int y[N], x[N];
double res[N];
int p[N];
int n, m, k, q;
 
void solve()
{
    int pos;
    cin >> pos;
    cin >> x[pos] >> y[pos];
    for (int i = 1; i <= m; ++i) 
        p[i] = n + 1; 
 
    for (int i = 1; i <= k; ++i)
        p[y[i]] = min(p[y[i]], x[i]);
 
    for (int i = 1; i <= m; ++i)
        res[i] = p[i] - 1;
 
    double k = 0; //斜率
    for (int i = 2; i <= m; ++i)
    {
        k = max(k, double(i - 1) / p[i]);
        res[i] = min(res[i], double(i - 1) / k - 1e-7);
    }
 
    k = 0;
    for (int i = m - 1; i > 0; --i)
    {
        k = max(k, double(m - i) / p[i]);
        res[i] = min(res[i], double(m - i) / k - 1e-7);
    }
 
    long long ans = 0;
    for (int i = 1; i <= m; ++i)
        ans += (int)res[i];
    
    cout << ans << '\n';
}

D

题意

解题思路

核心代码

G

题意

解题思路

核心代码

I

题意

解题思路

核心代码

J

题意

解题思路

核心代码

第五场

B

题意

一个人去钟表店买表,给出每块表的价格,如果购买k块表,每块表花费ai + k * i (i 为原来清单上的位置),问m元最多买几块表

解题思路

看到最大数量,可以想到二分,根据题意要求只需额外保存原清单位置,每次二分时+k*i后排序取前k个,时间复杂度为O(NlogNlogN)

核心代码

const int N = 1e5 + 10;
int n, m;
ll a[N], b[N];
 
bool check(ll k)
{
    for (ll i = 1; i <= n; ++i)
        b[i] = a[i] + k * i;
 
    sort(b + 1, b + n + 1);
    ll res = 0;
    for (int i = 1; i <= k; ++i)
        res += b[i];
 
    return res <= m;
}
 
void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
 
    int l = 0, r = n;
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l;
}

C

题意

NIO对长度为N的二进制串进行3N次问询,询问第k个数是否为1,若其中最多有一个回答错误,找出原二进制串是什么,若不存在则返回-1。

解题思路

模拟操作即可,使用bool来保存犯错次数,进行判断即可

核心代码

const int N = 1e5 + 10;
int a[N][2], n;
string res;
void solve()
{
    cin >> n;
    for (int i = 0; i < n * 3; ++i)
    {
        int q;
        string t;
        cin >> q >> t;
        if (t == "NO") ++a[q][0];
        else ++a[q][1];
    }
    bool error = false;
    for (int i = 0; i < n; ++i)
    {
        int j = 0;
        if (a[i][1] > a[i][0]) j = 1;
        int q = (j + 1) % 2;
        if (!a[i][j] && !a[i][q]) return cout << -1, void(0);
        else
        {
            while (a[i][q])
            {
                if (error) return cout << -1, void(0);
                error = true;
                --a[i][q];
            }
            res.push_back('0' + j);
        }
    }
 
    cout << res;
}

F

题意

平面上从下到上给出光盘的x, y, r,输出从上到下观察到的光盘的边长

解题思路

计算几何题QAQ
合理运用高中数学知识进行计算即可,注意弧度的合并。

核心代码

int n;
double res;
const double PI = acos(-1);
struct yuan
{
    double x, y, r;
} a[1010];
 
pdd get_rou(yuan &a1, yuan &a2)
{
    if (a1.x == a2.x && a1.y == a2.y)
    {
        if (a1.r <= a2.r) return {0, 2 * PI};
        else return {0, 0};
    }
    double d = sqrt((a2.y - a1.y) * (a2.y - a1.y) + (a2.x - a1.x) * (a2.x - a1.x));
    // cout << d << '\n';
    if (d >= a1.r + a2.r) return {0, 0};
    if (d <= a2.r - a1.r) return {0, 2 * PI};
    if (d <= a1.r - a2.r) return {0, 0};
     
    double t1 = atan2(a2.y - a1.y, a2.x - a1.x);
 
    double t2 = acos((a1.r * a1.r + d * d - a2.r * a2.r) / (2.0 * a1.r * d));
    // cout << d << ' ' << t1 << ' ' << t2 << '\n';
    // cout << t1 - t2 << ' ' << t1 + t2 << '\n';
    return {t1 - t2, t1 + t2};
}
 
void solve()
{
    cin >> n;
    for (int i = 0; i < n; ++i)
        cin >> a[i].x >> a[i].y >> a[i].r;
    for (int i = 0; i < n; ++i)
    {
        double dd = PI + PI;
        vector<pdd> circle;
        for (int j = i + 1; j < n; ++j)
        {
            // if (j == i) continue;
            pdd temp = get_rou(a[i], a[j]);
            if (temp.first < 0.0 && temp.second < 0.0) temp.first += dd, temp.second += dd;
            // cout << temp.first << ' ' << temp.second << '\n';
            if (temp.first < 0.0) circle.push_back({0.0, temp.second}), circle.push_back({dd + temp.first, dd});
            else if (temp.second > dd) circle.push_back({temp.first, dd}), circle.push_back({0, temp.second - dd});
            else circle.push_back(temp);
        }
        sort(circle.begin(), circle.end());
         
        //处理  
        for (int j = 0, q = 0; j < circle.size(); ++j)
        {
            if (circle[j].first > circle[q].second)
            {
                dd -= circle[q].second - circle[q].first;
                q = j;
            }
            else circle[q].second = max(circle[q].second, circle[j].second);
            if (j == circle.size() - 1) dd -= circle[q].second - circle[q].first;
        }
 
        res += dd * a[i].r;
        // cout << dd << ' ' << dd * a[i].r << '\n';
    }
    printf("%.9lf", res);
}

G

题意

查询字符串中,以k、f、c结尾的回文串数量

解题思路

马拉车模板题,略

核心代码

const int N = 1e6 + 100;
int n;
char s[N],str[N];
int num[N][3];
ll res[3];
int len1,len2,p[N],ans;
 
void init() //把原数组进行处理 
{
    str[0]='$'; //字符串开头加上特殊字符
    str[1]='#';
    for(int i=0; i<len1; i++)
    {
        str[i*2+2]=s[i];
        str[i*2+3]='#';
    }
    len2=len1*2+2;
    str[len2]='#';  //字符串末尾也加上特殊字符(和开头不同的)
 
    for (int i = 1; i <= len2; ++i)
    {
        for (int j = 0; j < 3; ++j)
            num[i][j] = num[i - 1][j];
        if (str[i] == 'k') num[i][0]++;
        else if (str[i] == 'f') num[i][1]++;
        else if (str[i] == 'c') num[i][2]++;
    }
}
 
void manacher()  //正片开始,马拉车
{
    int id=0,mx=0; //id表示回文子串的中心位置,mx表示回文子串的末尾位置。
    for(int i=1; i<len2; i++)
    {
        if(mx>i)
            p[i]=min(p[2*id-i],mx-i); //这一步是马拉车的关键部分,后面我在重点讲解。
        else
            p[i]=1;
        for(; str[i+p[i]]==str[i-p[i]]; p[i]++); //向两边扩展,匹配。
        if(p[i]+i>mx)
        {
            mx=p[i]+i;
            id=i;
        }
    }
}
 
void solve()
{
    cin >> n;
    cin >> s;
    len1 = strlen(s);
    init();
    manacher();
 
    for (int i = 1; i < len2; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            res[j] += (num[i + p[i] - 1][j] - num[i - p[i]][j] + 1) / 2;
        }

    for (int i = 0; i < 3; ++i) cout << res[i] << ' ';
}

H

题意

求∣x∣+∣y∣+∣x+y∣≤ n 和 半径为n / 2的圆重叠面积

解题思路

画图可看出,题目是平行四边形于圆的重叠,简单计算即可。

核心代码

void solve()
{
    double n;
    cin >> n;
    printf("%.9lf", (acos(-1) + 4.0) * n * n / 8.0);
}

K

题意

抽屉里有n对耳机,Yasa拿了k对耳机, NIO要拿几个耳机才能确保比Yasa的副数多

解题思路

文字游戏,注意是拿几个耳机,而且NIO是随机拿的。

核心代码

int main(){
    long long N,k;
    cin>>N>>k;
    if(2*k+1>N)cout<<-1<<endl;
    else cout<<N+1<<endl;
}

第八场

F

题意

给定两个序列s和t,求最长公共子序列长度,子序列元素为其中x = (ax^2 + bx + c) mod p,si / ti = x;

解题思路

有序列构造方式可观察到若s、t有一个元素相同,则其后所有元素均相同。故通过map保存s中每个数先出现的位置,再遍历t查询相同元素计算最长子序列。

核心代码

int n, m;
ll p, x, a, b, c;

void solve()
{
    int res = 0;
    map<int, int> st;
    cin >> n >> m >> p >> x >> a >> b >> c;
    for (int i = 0; i < n; ++i)
    {
        x = (a * (x * x % p) % p + b * x % p + c) % p;
        int t = (int)x;
        if (st.find(t) == st.end()) st[t] = i;
    }
    for (int i = 0; i < m; ++i)
    {
        x = (a * (x * x % p) % p + b * x % p + c) % p;
        int t = (int)x;
        if (st.find(t) != st.end()) res = max(res, min(m - i, n - st[t]));
    }    
    cout << res << '\n';
}

第九场

A

题意

给定一个数组a,找出所有能包含M种数的区间个数。

解题思路

首先使用一个数组保存下当前区间每个数的个数查看是否有解,由贪心的思路可以想到当一个区间满足条件时,包含该区间的区间也一定满足条件,故采用双指针,从左往右遍历,每次找到一个满足条件的最小区间然后直接加上以当前左指针为边界的剩余区间即可。

核心代码

const int N = 1e5 + 10;
int a[N], t[N], n, m;
ll res;
void solve()
{
    int type = 0;
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
    {
        cin >> a[i];
        if (!t[a[i]]) ++type;
        ++t[a[i]];
    }
 
    if (type != m) return cout << "0", void(0);
 
    for (int i = 0, j = n - 1; i < n; )
    {
        if (i && !t[a[i - 1]])
        {
            while (j < n - 1 && !t[a[i - 1]])
            {
                ++j;
                ++t[a[j]];
            }
            if (!t[a[i - 1]]) break;
        }
        else while (i <= j && t[a[j]] > 1)
        {
            --t[a[j]];
            --j;
        }
        res += n - j;
        t[a[i]]--;
        ++i;
    }
    cout << res;
}

B

题意

有两个人从点1开始跳,每次可以跳(0,a_i]步,问两人在相同步数下跳到点n的概率

解题思路

首先可以想到暴力的思路,即模拟每一步,遍历所有的点。
可以想到若是将每个点的概率依次加到后面的点上,最后的复杂度是n^3,显然不可以。
观察到每个点对之后点的影响相同,因此可以使用差分来优化,最后复杂度为n^2
PS:QAQ赛时没预处理逆元疯狂T

核心代码

const int N = 8010;
const ll mod = 998244353;
ll a[N], b[N], res;
int n;
vl dp(N), tt(N);
 
void solve()
{
    cin >> n;
    dp[1] = 1;
    for (int i = 1; i < n; ++i)
    {
        cin >> a[i];
        b[i] = inv(a[i], mod);
    } 
    for (int j = 1; j < n; ++j)
    {
        for (int i = j; i <= n; ++i) tt[i] = 0;
        for (int i = j; i < n; ++i)
        {
            ll temp = dp[i] * b[i] % mod;
            tt[i + 1] = (tt[i + 1] + temp) % mod;
            tt[i + a[i] + 1] = (tt[i + a[i] + 1] + mod - temp) % mod;
        }
        for (int i = j; i <= n; ++i) tt[i] = (tt[i - 1] + tt[i]) % mod;
        dp = tt;
        res = (res + tt[n] * tt[n]) % mod;
    }
    cout << res;
}

G

题意

给出k个字符串,求出有几个不同的公共回文子串。

解题思路

用马拉车跑一遍回文子串,hash后放入map里查找即可(赛时被卡自然溢出的hash了QAQ)

核心代码

const int N = 6e5 + 10; //字符串长度
char str[N];
string a;
int len1, len2, p[N]; //半径

int k;
// int res; //最长回文长度
ull h[2][N], tp[2][N], P[2] = {131, 13331};
ull mod = 1e9 + 7;
map<pull, int> st; 

// 计算子串 str[l ~ r] 的哈希值
inline pull get(const int &l, const int &r)
{
    if (l == 0) return {h[0][r], h[1][r]};
    return {(h[0][r] - h[0][l - 1] * tp[0][r - l + 1] % mod + mod) % mod, (h[1][r] - h[1][l - 1] * tp[1][r - l + 1] % mod + mod) % mod};
}

void init()
{
    for (int i = 0; i < 2; ++i)
    {
        h[i][0] = str[0];
        for (int j = 1; j < len2; ++j)
            h[i][j] = (h[i][j  - 1] * P[i] + str[j]) % mod;
    }
}

void Manachar(const int &x)
{
    int id = 0, mx = 0;
    len1 = a.size();
    len2 = len1 + len1 + 2;

    str[0] = '$';
    str[1] = '#';
    for (int i = 0; i < len1; ++i)
    {
        str[i + i + 2] = a[i];
        str[i + i + 3] = '#';
    }
    str[len2] = '\0';

    init();

    for (int i = 0; i < len2; ++i)
    {
        if (mx > i) p[i] = min(mx - i, p[id + id - i]);
        else p[i] = 1;
        if (str[i - p[i] + 1] >= 'a' && str[i - p[i] + 1] <= 'z') st[get(i - p[i] + 1, i + p[i] - 1)] |= 1 << x;

        while (str[i + p[i]] == str[i - p[i]])
        {
            if (str[i - p[i]] >= 'a' && str[i - p[i]] <= 'z') st[get(i - p[i], i + p[i])] |= 1 << x;
            ++p[i];
        } 

        if (i + p[i] > mx)
        {
            mx = i + p[i];
            id = i;
            // res = max(res, p[i] - 1);
        }
    }
}        


int main()
{
    int res = 0;
    for (int i = 0; i < 2; ++i)
    {
        tp[i][0] = 1;
        for (int j = 1; j < N; ++j)
            tp[i][j] = tp[i][j - 1] * P[i] % mod;         
    }

    cin >> k;
    for (int i = 0; i < k; ++i)
    {
        cin >> a;
        Manachar(i);
    }

    int pp = (1 << k) - 1;
    for (auto t : st)
    {
        if (t.second == pp) ++res;
    }
    cout << res << '\n';

    
}

加赛

E

题意

有一个“复读”比赛,共有n个人参加,可进行任意轮次,每轮大家依次进行发言,每人只有一次发言机会,可以选择不发言,若当前轮次无人参加或剩下人数不到k个则游戏结束,其中发言的人能得到 a_i_j 的分数, (j为该轮次第几个发育),最后倒数第k个复读的人会减分,大家都想使自己的分数最大化,问最后的得分情况。

解题思路

由于发言威慑(看我顶不顶你就完事了)当剩下k个人时没有人敢发言,如果发言了其他人就会把他顶上去,0分比负分更好,由该点出发,2k个人时也没人敢发言(瑟瑟发抖.jpg),因此发言人数只能有n%k个,此时就是手快有手慢无,能发言的一定会发言,因此只有前n%k个人有分,且在一轮结束游戏。

核心代码

const int N = 1e3 + 10, mod = 1e9 + 7;
int a[N][N];

void solve()
{
	int n,p;
	cin>>n>>p;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)cin>>a[i][j];
	}
	int t=0;
	vector<int> b(n+1);
    int j = 1;
	for(int i=1;i<=n%p;i++){
		b[i] = a[i][j++];
	}
	for(int i=1;i<=n;i++)cout<<b[i]<<' ';

}

H

题意

在n个点的树上,根为1,给出一个x,求f(x)的后缀0数量请添加图片描述

解题思路

在一个乘法式子中,要想找到后缀0的数量则要确定将各个乘数分解质因数后,找到有几对(2,5),即min(num(2), num(5))。
首先一个结点与其子孙节点的lca为其本身,与其他点的lca完全可以有父节点继承过来。因此开始预处理下每个结点的子孙节点数量,接着从点1开始dfs,向孩子结点转移乘法中2和5的数量,每个数的分解也可以先预处理好。

核心代码

const int N = 1e5 + 10;
int a[N], res[N], n, q;
bool st[N];
int qt[N][8];
vi h[N];
int prime[N][8];
 
void init()
{
    for (int i = 1; i < N; ++i)
    {
        int q = i;
        while (q % 2 == 0)
        {
            ++prime[i][2];
            q /= 2;
        }
        while (q % 5 == 0)
        {
            ++prime[i][5];
            q /= 5;            
        }
    }
}
 
int dfs(const int& x)
{
    for (int t : h[x])
    {
        if (!st[t])
        {
            st[t] = true;
            a[x] += dfs(t);
        }
    }
    ++a[x];
    return a[x];
}
 
void get_res(const int &x)
{
    for (int t : h[x])
    {
        if (!st[t])
        {
            st[t] = true;
            qt[t][2] = qt[x][2] + a[t] * prime[t][2] - a[t] * prime[x][2];
            qt[t][5] = qt[x][5] + a[t] * prime[t][5] - a[t] * prime[x][5];         
            get_res(t);
        }
    }
    res[x] = min(qt[x][2], qt[x][5]);
}
 
void solve()
{
    init();
    cin >> n >> q;
    for (int i = 1; i < n; ++i)
    {
        int u, v;
        cin >> u >> v;
        h[u].push_back(v);
        h[v].push_back(u);
    }
    st[1] = true;
    dfs(1);
 
    memset(st, 0, sizeof(st));
    st[1] = true;
    get_res(1);
 
    while (q--)
    {
        int x;
        cin >> x;
        cout << res[x] << '\n';
    }
}

J

题意

给出一个从0下标开始的数组,可以任意的选择数字i,如果请添加图片描述使得ai = (ai + 1) % 3。问是否有可能使得整个数组数字相等。

解题思路

首先可以想到将相邻且相同的数字看成一个,同时若出现101,212,020这样的组合应将中间的化为与左右相同的。在经过这样的合并后,数组中只会剩下0、1、3(0 1 2 / 2 1 0)个元素,显然仅有 2 1 0的情况无法满足。可以使用双端队列进行操作

核心代码

const int N = 1e6 + 10;
int n;
deque<int> a;
 
bool check()
{
    if (a.size() <= 1) return true;
 
    for (int i = 0; i < 3; ++i)
    {
        int x = a.back();
        a.pop_back();
        if ((x + 2) % 3 != a.front()) return true;
        else a.push_front(x);
    }
    return false;
}
 
void solve()
{
    a.clear();
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        int x;
        cin >> x;
        a.push_back(x);
    }
    //合并
    for (int i = 1; i <= a.size(); ++i)
    {
        if (a.size())
        {
            if (a.back() == a.front())
            {
                i = 0;
                a.pop_front();
            } 
            else
            {
                a.push_front(a.back());
                a.pop_back();
            }             
             
        }
 
    }
    for (int i = 1; i <= a.size(); ++i)
    {
        if (a.size() > 1)
        {
            int x = a.back();
            a.pop_back();
            if ((x + 1) % 3 == a.back() && a.back() == a.front())
            {
                a.pop_front();
                i = 0; 
            } 
            else a.push_front(x);    
        }
    }
 
    if (!check()) cout << "No\n";
    else cout << "Yes\n";
}

M

题意

给出个游戏得分要求,求出得分

解题思路

根据题意模拟即可,略

核心代码

double a[5][6], A, B, A0, B0;
double pre[5];
void work()
{

    for (int i = 1; i <= 4; i++)
    {
        for (int j = 1; j <= 5; j++)
        {
            cin >> a[i][j];
            pre[i] += a[i][j];
        }
    }

    A = pre[1] * 1 + pre[2] * 2 + pre[3] * 3 + pre[4] * 5;
    B = pre[4];
    
    A0 = a[1][1] * 1 + a[1][2] * 1 + a[1][3] * 0.8 + a[1][4] * 0.5 
    + a[2][1] * 2 + a[2][2] * 2 + a[2][3] * 1.6 + a[2][4] * 1.0 + 
    + a[3][1] * 3 + a[3][2] * 3 + a[3][3] * 2.4 + a[3][4] * 1.5 + 
    + a[4][1] * 5 + a[4][2] * 5 + a[4][3] * 2.5 + a[4][4] * 2.0;
    B0 = a[4][1] * 1 + a[4][2] * 0.5 + a[4][3] * 0.4 + a[4][4] * 0.3;
    
    double res = (A0 / A + B0 / B * 0.01) * 100;
    printf("%.9lf", res);
}

第十场

E

题意

有n个人m项任务,每个人有自己擅长的任务,需保证每个人都能仅接到一个任务的情况下,单个任务可被多个人接到,在所有可能的情况下,选取任务接受至少1次的最多,至少2次的最多…

解题思路

题解时最大流算法不太懂QAQ,看到这种匹配的情况可以想到匈牙利算法,原算法时一对一,而此时是多对一,因此可进行多轮,每轮中让任务去选人,直到所有人都分配好任务。

核心代码

bool h[N][N], st[N];
char temp[N];
int n, m, match[N];

bool find(const int& x)
{
    for (int j = 1; j <= n; ++j)
    {
        if (!st[j] && h[j][x])
        {
            st[j] = true;
            if (!match[j] || find(match[j]))
            {
                match[j] = x;
                return true;
            }
        }
    }
    return false;
}

int main(void)
{
    ios::sync_with_stdio(0); std::cin.tie(nullptr); std::cout.tie(nullptr);
    cin >> n >> m;
    bool t = true;
    for (int i = 1; i <= n; ++i)
    {
        cin >> (temp + 1);
        bool f = false;
        for (int j = 1; j <= m; ++j)
        {
            h[i][j] = temp[j] - '0';
            f |= h[i][j];
        }
        t &= f;
    }
    if (!t) return cout << -1, 0;
    int cnt = n;
    while (cnt)
    {
        for (int i = 1; i <= m; ++i)
        {
            memset(st, 0, sizeof(st));
            cnt -= find(i);
        }
    }
    for (int i = 1; i <= n; ++i) cout << match[i] << ' ';
    return 0;
}

F

题意

在一场有关图的游戏中,给出起点和终点,两个人轮流进行操控,其中Join可沿边走到下一个点,并删去这条边,走到终点即为获胜;Cut可减去这个点相邻的边,成功阻止Join即为获胜。Cut先手。

解题思路

由于Cut先手,因此只要一个点有两条边可以成功通向终点,该点即可获胜。

核心代码

const int N = 110;
bool st[N];
vi h[N];
int num[N];
int n, m, s, t;
 
void bfs()
{
    st[t] = true;
    queue<int> qu;
    qu.push(t);
    while (qu.size())
    {
        int q = qu.front();
        qu.pop();
        for (int x : h[q])
        {
            ++num[x];
            if (num[x] >= 2 && !st[x])
            {
                st[x] = true;
                qu.push(x);
            }
        }
    }
}
 
void solve()
{
    memset(num, 0, sizeof(num));
    memset(st, 0, sizeof(st));
    cin >> n >> m >> s >> t;
    for (int i = 1; i <= n; ++i) h[i].clear();
 
    while (m--)
    {
        int u, v;
        cin >> u >> v;
        h[u].push_back(v);
        h[v].push_back(u);
    }
 
    bfs();
 
    if (st[s]) cout << "Join Player\n";
    else cout << "Cut Player\n";
}

H

题意

在游戏中A、B分别有几个随从,现在有一个技能每轮随机攻击一个人(随从)-10HP,直到A或B死亡。问A获胜的概率是多少。

解题思路

显然随从并不影响概率(因为大家的随从是不影响结果的)因此只考虑A、B即可。

核心代码

const ll mod = 998244353;
const int N = 2e6 + 10;
int a[7], b[7], A, B;
ll ji[N], ni[N];
ll res;


inline ll C(const int& a, const int& b)
{
    return ji[a] * ni[b] % mod * ni[a - b] % mod;  
}

void solve()
{
    ll inv2 = inv(2, mod);
    ji[0] = ni[0] = 1;
    for (ll i = 1; i < N; ++i)
    {
        ji[i] = ji[i - 1] * i % mod;
        ni[i] = ni[i - 1] * inv(i, mod) % mod;
    }

    cin >> A;
    A = (A + 9) / 10;
    for (int i = 0; i < 7; ++i) cin >> a[i];
    cin >> B;
    B = (B + 9) / 10;
    for (int i = 0; i < 7; ++i) cin >> b[i];

    for (int i = 0; i < A; ++i)
    {
        res = (res + C(B - 1 + i, i) * ksm(inv2, B - 1 + i, mod)) % mod;
    }
    res = res * inv2 % mod;
    cout << res << '\n';
}

I

题意

给出两个数组A、B,找出一组| a_i - a_j | = |b_k - b_ l|。
其中n,m <= 1e6; a_i, b_i <= 1e7

解题思路

在给A、B数组排序后,可将绝对值去掉变成 a_i - a_j = b_k - b_ l, 即a_i + b_ l = a_j + b_k
因为a <= 1e7, 所有sum <= 2e7可直接暴力查找,注意去重即可。

核心代码

const int N = 1e6 + 10, M = 2e7 + 10;
map<int, int> a, b;
int n, m;
pii st[M];
pii aa, bb;

inline bool operator!=(const pii& a, const pii& b)
{
    return a.first != b.first || a.second != b.second;
}

int main(void)
{
    ios::sync_with_stdio(0); std::cin.tie(nullptr); std::cout.tie(nullptr);
    cin >> n >> m;
    
    for (int i = 1; i <= n; i++)
    {
        int temp;
        cin >> temp;
        if (a[temp]) aa = {a[temp], i};
        else a[temp] = i;
    } 
    for (int i = 1; i <= m; i++)
    {
        int temp;
        cin >> temp;
        if (b[temp]) bb = {b[temp], i};
        else b[temp] = i;
    } 
    if (aa != make_pair(0, 0) && bb != make_pair(0, 0)) return cout << aa.first << ' ' << aa.second << ' ' << bb.first << ' ' << bb.second, 0;

    for (pii x : a)
    {
        for (pii y : b)
        {
            int sum = x.first + y.first;
            if (st[sum] != make_pair(0, 0)) return cout << st[sum].first << ' ' << x.second << ' ' << st[sum].second << ' ' << y.second, 0;
            else st[sum] = {x.second, y.second};
        }
    }
    cout << -1;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝尘JC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值