【ACM-ICPC】Nowcoder Summer Training Camp 3

B.Black and white

题意

给定一个矩阵,我们可以取矩阵中的数。对于每个2*2矩阵,若有三个数被取,则第四个数免费取,问取走整个矩形最小花费?

解法分析

矩阵中最少选n + m - 1个数,满足,矩阵每个行要有一个,每个列要有一个。
证明:先任意取一个三角形,要进行扩展就会在当前有的矩形周围扩展,每行每列都至少需要一个来进行扩展,所以最少取n + m - 1个数。
对于此问题的解决,我们将行和列都看做点来看,每行每列都要有一个,取n + m + 1个数,每个数当做两点边权,形成一个完全图。对完全图做一个最小生成树就可以完成。
tip:完全图过大,桶排序+kruscal。

代码

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int x, y;
};
vector<node> bas[100010];
int fa[100010];
int get_fa(int x)
{
    return x == fa[x] ? x : (fa[x] = get_fa(fa[x]));
}
int main()
{
    int n, m, a, b, c, d, p;
    cin >> n >> m >> a >> b >> c >> d >> p;
    for (int i = 1; i <= n + m; ++i)
        fa[i] = i;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            a = (1LL * a * a * b + 1LL * a * c + d) % p;
            bas[a].push_back({i, j + n});
        }
    long long ans = 0;
    for (int i = 0; i <= p; ++i)
        for (auto e : bas[i])
        {
            int fax = get_fa(e.x), fay = get_fa(e.y);
            if (fax != fay)
            {
                ans += i;
                fa[fax] = fay;
            }
        }
    cout << ans;
    return 0;
}

小结

总想贪心。。。。。。。。。。。。
太贪了

E. Math

题意

给定n,求1-n范围内有多少(x, y)满足 (x 2 + y 2) 能整除 (xy + 1)。(x <= y)
n范围:1e18.

解法分析

考虑方程 \(x^2 + y^2 = k(xy + 1)\),固定x和k,由韦达定理可以推出对于某个解y,我们可以得到另一个解为kx-y。那么我们固定这个k,解放x,可知绝对值最小值为kx-y=0,解得y=x3,相当于说,我们找每个x,可以找到k=x2,然后绝对值最小解为(x, x3),同样韦达定理,可以得出x和ky-x为两个解,而且ky-x > y,所以,可以由(x,y)->(y,ky-x),所以我们枚举x范围1e6,找到k,然后套娃递推。
思考:为啥这样的结果是完备的?

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<LL> a;
int main()
{
    LL t, n;
    a.push_back(1);
    for (LL i = 2; i <= 1e6; ++i)
    {
        LL x = i, y = x * x * x, k = x * x;
        while (y <= 1e18)
        {
            a.push_back(y);
            if ((x + 1e18) / k < y)
                break;
            LL t = x;
            x = y;
            y = k * y - t;
        }
    }
    sort(a.begin(), a.end());
    cin>>t;
    while (t--)
    {
        cin>>n;
        LL ans = upper_bound(a.begin(), a.end(), n) - a.begin();
        cout << ans << endl;
    }
    return 0;
}

小结

数学!!!!!好难a

F. 24dian

题意

n张扑克卡片(1到13),凑到m的所有牌面组合,其中,牌面组合的所有解法都必须包含分数(小数)。
1<= n <= 4.

解法分析

n <= 3无解法,易得。
只考虑经典四张牌m点玩法,进行大模拟枚举。

小结

题意读错两次,细心一点吧。

G. Yu Ling(Ling Yuezheng) and Colorful Tree

题意

给定n个点的树,q次操作。
0 u x将树上u染成x,染色对点对色无重复。
1 u l r x找出u的祖先中染色[l, r]且为x的倍数的结点与u的最小距离,并输出,若无则输出Impossible!。

解法分析

0操作不说。对于1操作因为染色无重复,所以对于每个点记一个色,每个颜色记一个点,在[l, r]中找x的倍数且为u的祖先的,得到最小距离即可。
找u的祖先采用dfn时间戳的方法。暴力解法就是[l, r]中直接找,并且侥幸能过这题。
正解是采用分块+bitset,由于笔者太菜,不会做,先写个暴力,正解咕咕咕咕。。。。。。(轻喷qaq)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int link[200010];
int tot = 0, now;
struct edge
{
    int y, next;
    LL v;
} a[300010];
int in[200010], out[200010], col[200010], ver[200010];
LL dep[200010];
void insert(int x, int y, LL v)
{
    a[++tot] = {y, link[x], v};
    link[x] = tot;
}
void dfs(int x)
{
    in[x] = ++now;
    for (int i = link[x]; i; i = a[i].next)
    {
        int y = a[i].y;
        if (!in[y])
        {
            dep[y] = dep[x] + a[i].v;
            dfs(y);
        }
    }
    out[x] = ++now;
}
int main()
{
    int n, q;
    cin >> n >> q;
    for (int i = 1; i < n; ++i)
    {
        int x, y;
        LL v;
        cin >> x >> y >> v;
        insert(x, y, v);
        insert(y, x, v);
    }
    now = 0;
    dep[1] = 0;
    dfs(1);
    // for (int i = 1; i <= n; ++i)
    //     cout << dep[i] << endl;
    while (q--)
    {
        int opt, u, x, l, r;
        cin >> opt;
        if (!opt)
        {
            cin >> u >> x;
            col[u] = x;
            ver[x] = u;
        }
        else 
        {
            cin >> u >> l >> r >> x;
            int st = (l + x - 1) / x * x;
            LL ans = 1e18;
            for (int i = st; i <= r; i += x)
            {
                if (in[ver[i]] <= in[u] && out[u] <= out[ver[i]])
                    ans = min(ans, dep[u] - dep[ver[i]]);
            }
            if (ans == 1e18)
                cout << "Impossible!" << endl;
            else 
                cout << ans << endl;
        }
    }
    return 0;
}

小结

分块,挖坑。dfn,好东西。

I. Kuriyama Mirai and Exclusive Or

题意

现有一数组\(a_n\)
要进行q次操作,操作分两种:
0 l r x:下标区间[l, r]上的数组元素异或x,即:ai xor x , l <= i <= r
1 l r x:下标区间[l, r]上的数组元素异或x + (i - l),即:ai xor (x + i - l), l <= i <= r

题解

0操作很好做,建立差分异或数列b,0 l r x相当于把 b[l] 和 b[r+1] 异或上。
1操作不好做,每个数异或的数不一样。但如果我们将每个数看做30个二进制位,按二进制位来考虑,每个数异或的数是有周期规律的。
就比如第一位来说,x若二进制第一位为1,则 a[l] ^= 1, a[l+1] ^= 0, a[l+2] ^= 1……以1为周期,第二位以2为周期,第i位以\(2^i\)为周期。而且,每个周期的异或对于差分异或数组只要对每个周期第一个元素进行异或就行。
但我们第一个元素要异或一个x,所以要先考虑x对该周期的余数,处理掉前面多的部分就可以进行周期处理。
建立二维判定数组d[i][j]表示b[j]是否要异或\(2^i\)
维护过程:
对于1 l r x 处理x每一位,若为0,就找到下一个周期起始((((x>>i)+1)<<i)-x+l,让d[i][nxt]^=1,若为1,先让 b[l]^=1<<i,再做同样的操作。
上述过程中不考虑右边界,我们对l,xr,x+r-l+1分别进行同样操作即可抵消后面影响。
因为对于每个询问我们只处理了周期的第一个,但每个d[i][j]都是由d[i][j-(1<<i)]异或过来,所以根据此来完善d数组。

代码

#include<bits/stdc++.h>
using namespace std;
int n, q;
int a[600010], b[600010];
bool d[600010][35];
void change(int st, int x)
{
    if (st > n)
        return ;
    for (int i = 0; i <= 30; ++i)
    {
        if ((x & (1 << i)))
            b[st] ^= (1 << i);
        int nxt = st + ((((x >> i) + 1) << i) - x);
        if (nxt <= n)
            d[nxt][i] ^= 1;
    }
}
int main()
{
    cin >> n >> q;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    while (q--)
    {
        int o, l, r, x;
        cin >> o >> l >> r >> x;
        if (!o)
            b[l] ^= x, b[r + 1] ^= x;
        else
            change(l, x), change(r + 1, x + r - l + 1);
    }
    for (int i = 0; i <= 30; ++i)
        for (int j = 1; j <= n; ++j)
        {
            if (j + (1 << i) <= n) 
                d[j + (1 << i)][i] ^= d[j][i];
            if (d[j][i])
                b[j] ^= (1 << i);
        }
    for (int i = 1; i <= n; ++i)
    {
        b[i] ^= b[i - 1];
        cout << (a[i] ^ b[i]) << ' ';
    }
    return 0;
}

J.Counting Triangles

题意

完全无向图,n个点给定白边黑边,问有多少纯色三角形?
纯色三角形:三个点,两两连边颜色相同。

解法分析

总三角形数是 \(C_n^3\) 考虑非纯色三角形中每个点,有两个点对应连边是异色,那么统计每个点有多少对异色连边,就是对非纯色三角形的贡献,统计总数除二就是非纯色三角形个数。

代码

#include <bits/stdc++.h>
using namespace std;
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;
    cin >> 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();
    long long sum = 0;
    for (int i = 0; i < n; ++i)
    {
        int cnt1 = 0, cnt2 = 0;
        for (int j = 0; j < n; ++j)
        {
            if (i == j)
                continue;
            if (edge[i][j])
                cnt1++;
            else
                cnt2++;
        }
        sum += 1LL * cnt1 * cnt2;
    }
    cout << 1LL * n * (n - 1) * (n - 2) / 6 - sum / 2;
    return 0;
}
posted @ 2021-08-03 15:34  cacu  阅读(32)  评论(0编辑  收藏  举报