2021牛客暑期多校训练营3

2021牛客暑期多校训练营3

B、Black and White

居然是最小生成树。

n n n行抽象成 n n n个点, m m m列抽象成 m m m个点,分别放在两个集合中。一个点 ( i , j ) (i,j) (i,j)被染黑对应到两个集合 < i , j > <i,j> <i,j>​之间连一条边。若一条边加入后如果不改变连通性,说明 < i , j > <i,j> <i,j>间已经连通,即至少有 < i , k > , < p , k > , < p , j > <i,k>,<p,k>,<p,j> <i,k>,<p,k>,<p,j>三条边,则此时这条边可以以代价0加入。

求最小生成树即可。

#include <bits/stdc++.h>
typedef long long ll;
const int MAXN = 5e3 + 10;
ll dis[MAXN << 1];
ll A[MAXN * MAXN];
int main()
{
    int n, m, a, b, c, d, p; scanf("%d%d%d%d%d%d%d", &n, &m, &a, &b, &c, &d, &p);
    A[0] = a;
    for (int i = 1; i <= n * m; ++i)
        A[i] = (A[i - 1] * A[i - 1] * b % p + A[i - 1] * c % p + d) % p;
    memset(dis, 0x3f, sizeof dis);
    dis[1] = -1;
    for (int i = 1; i <= m; ++i)
        dis[n + i] = std::min(dis[n + i], A[i]);
    ll ans = 0;
    for (int i = 1; i <= n + m - 1; ++i)
    {
        int t = -1;
        for (int j = 1; j <= n + m; ++j)
            if (dis[j] != -1 && (t == -1 || dis[j] < dis[t]))
                t = j;
        ans += dis[t]; dis[t] = -1;
        if (t <= n)
        {
            for (int j = 1; j <= m; ++j)
                dis[n + j] = std::min(dis[n + j], A[m * (t - 1) + j]);
        }
        else
        {
            for (int j = 1; j <= n; ++j)
                dis[j] = std::min(dis[j], A[m * (j - 1) + t - n]);
        }
    }
    printf("%lld\n", ans);
    return 0;
}

E、Math

IMO原题。

x 2 + y 2 1 + x y \frac{x^2+y^2}{1+xy} 1+xyx2+y2是完全平方数,记为 k 2 k^2 k2

( x , y ) (x,y) (x,y)​​​满足 x 2 + y 2 1 + x y = k 2 \frac{x^2+y^2}{1+xy}=k^2 1+xyx2+y2=k2​​​​,那么 ( y , k 2 y − x ) (y,k^2y-x) (y,k2yx)​​​​也满足。

把所有满足 x 2 + y 2 1 + x y = k 2 \frac{x^2+y^2}{1+xy}=k^2 1+xyx2+y2=k2的解塞到一个数组 a k [ ] a_k[] ak[]里,观察有 a k [ 1 ] = k , a k [ 2 ] = k 3 a_k[1]=k,a_k[2]=k^3 ak[1]=k,ak[2]=k3

a k [ i ] = k 2 a k [ i − 1 ] − a k [ i − 2 ] a_k[i]=k^2a_k[i-1]-a_k[i-2] ak[i]=k2ak[i1]ak[i2]​​。把所有解打出来,记一下个数,之后跑前缀和搞定。

#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 1e6 + 10;
const ll INF = 1000000000000000000;
std::set < ll > alls;
int main()
{
    std::map < ll, ll > mp;
    mp[1]++; alls.insert(-1);
    for (ll i = 2; i <= MAXN; ++i)
    {
        ll p = i;
        if (i * i * i > INF) break;
        ll pp = i * i * i;
        mp[i * i * i]++; alls.insert(-i * i * i);
        for (int j = 2; ; ++j)
        {
            if (pp > (INF + p) / (i * i)) break;
            ll u = i * i * pp - p;
            if (u > INF) break;
            mp[u]++; alls.insert(-u);
            p = pp, pp = u;
        }
    }
    auto p = mp.begin();
    auto q = p; ++q;
    for ( ; q != mp.end(); ++q)
    {
        q -> second += p -> second;
        ++p;
    }
    int t; scanf("%d", &t);
    while (t--)
    {
        ll n; scanf("%lld", &n);
        printf("%lld\n", mp[-(*alls.lower_bound(-n))]);
    }
    return 0;
}

J、Counting Triangles

每个点计算黑边乘白边并求和。这样每个异色三角形都会被算两遍,总数减去二分之一的和即可。

我们队卡了好久,想通之后大骂sb题。

#include <bits/stdc++.h>
typedef long long ll;
namespace GenHelper
{
    unsigned z1, z2, z3, z4, b, u;
    unsigned get()
    {
        b = ((z1 << 6) ^ z1) >> 13;
        z1 = ((z1 & 4294967294U) << 18) ^ b;
        b = ((z2 << 2) ^ z2) >> 27;
        z2 = ((z2 & 4294967288U) << 2) ^ b;
        b = ((z3 << 13) ^ z3) >> 21;
        z3 = ((z3 & 4294967280U) << 7) ^ b;
        b = ((z4 << 3) ^ z4) >> 12;
        z4 = ((z4 & 4294967168U) << 13) ^ b;
        return (z1 ^ z2 ^ z3 ^ z4);
    }
    bool read()
    {
        while (!u) u = get();
        bool res = u & 1;
        u >>= 1;
        return res;
    }
    void srand(int x)
    {
        z1 = x;
        z2 = (~x) ^ 0x233333333U;
        z3 = x ^ 0x1234598766U;
        z4 = (~x) + 51;
        u = 0;
    }
}
using namespace GenHelper;
bool edge[8005][8005];
int main()
{
    int n, seed;
    scanf("%d%d", &n, &seed);
    srand(seed);
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
            edge[j][i] = edge[i][j] = read();
    ll ans = 0;
    for (int i = 0; i < n; ++i)
    {
        int black = 0;
        for (int j = 0; j < n; ++j)
            if (edge[i][j] == 1 && i != j) ++black;
        ans += 1ll * black * (n - 1 - black);
    }
    printf("%lld\n", 1ll * n * (n - 1) * (n - 2) / 6 - (ans >> 1));
    return 0;
}

快一年没写博客,翻了翻去年的多校,发现还是一样菜。

不要问为什么没有多校1和2,问就是被课设折磨。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值