T1 动物园

题意

给定一个带点权图,求每两个点之间路径上最小点权的最大值之和。

怎么做

点权下方边权跑最大生成树(边权是两个点中的最小值),这样就可以保证每条路径都是两个点之间最小点权的最大值。
考虑统计答案,这个路径对答案的贡献是两边经过他的点的个数 * 2。在跑最小生成树的时候使用带权并查集维护两边点的数量即可。

#include <bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target ("avx2")
namespace fdata
{
inline char nextchar()
{
    static const int BS = 1 << 21;
    static char buf[BS], *st, *ed;
    if (st == ed)
        ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename Y>
inline void poread(Y &ret)
{
    ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;
    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
using namespace std;
const int MAXN = 1e5 + 10;
const int MAXM = 1e6 + 10;
int n, m;
long long ans = 0;
int val[MAXN];
int a[MAXN], siz[MAXN];
inline void init()
{
    for (register int i = 1; i <= n; ++i)
        a[i] = i;
    for (register int i = 1; i <= n; ++i)
        siz[i] = 1;
}
int find(int x)
{
    return a[x] == x ? x : a[x] = find(a[x]);
}
struct road
{
    int x, y, w;
    inline bool operator<(const road &t) const
    {
        return w > t.w;
    }
} r[MAXM];
int main()
{
    poread(n), poread(m);
    init();
    for (register int i = 1; i <= n; ++i)
        poread(val[i]);
    for (register int i = 1; i <= m; ++i)
    {
        poread(r[i].x), poread(r[i].y);
        r[i].w = min(val[r[i].x], val[r[i].y]);
    }
    sort(r + 1, r + m + 1);
    for (register int i = 1; i <= m; ++i)
    {
        register int x = find(r[i].x);
        register int y = find(r[i].y);
        if (x == y)
            continue;
        ans += siz[x] * siz[y] * r[i].w * 2ll;
        a[x] = y;
        siz[y] += siz[x];
    }
    printf("%lld\n", ans);
}

T2 线段计数

题意

插入或删除线段,询问插入的线段覆盖了几个线段。线段长度递增

怎么做

我们考虑线段完全覆盖某一个线段的情况:

R >= r && l >= L
这样我们开两个树状数组维护区间左端点个数以及区间右端点个数。每次查询时答案就是 右端点左侧的右端点个数 减去 左端点左侧的左端点个数。
这个方法会在下面这个情况出锅:

但由于数据保证了线段长度单调递增,这个情况不存在啦啦啦。

建议:贪婪大陆

//多测不清空,爆零两行泪
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("avx2")
namespace fdata
{
inline char nextchar()
{
    static const int BS = 1 << 21;
    static char buf[BS], *st, *ed;
    if (st == ed)
        ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename Y>
inline void poread(Y &res)
{
    res = 0;
    bool bo = 0;
    char c;
    while (((c = nextchar()) < '0' || c > '9') && c != '-')
        ;
    if (c == '-')
        bo = 1;
    else
        res = c - 48;
    while ((c = nextchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo)
        res = ~res + 1;
    // std::cerr << res << std::endl;
}
#undef nextcar
} // namespace fdata
using fdata::poread;
using namespace std;
namespace lky
{
const int MAXN = 2e5 + 10;
int n, m, tot;
//封装是个好东西
class T
{
public:
    int t[MAXN << 1];
    inline void init() { memset(t, 0, sizeof(t)); }
    inline void add(int x)
    {
        for (; x <= m; x += (x & -x))
            ++t[x];
    }
    inline void del(int x)
    {
        for (; x <= m; x += (x & -x))
            --t[x];
    }
    inline int ask(int x)
    {
        register int res = 0;
        for (; x; x -= (x & -x))
            res += t[x];
        return res;
    }
} t1, t2;
struct node
{
    int opt, x, y;
} opt[MAXN];
//根本不用开两个结构体分别记录操作和序列,只要这样映射一下
int po[MAXN];
int b[MAXN << 1];
inline int find(const int &x)
{
    register int l = 1, r = m, res = 1, mid;
    while (l <= r)
    {
        mid = (l + r) >> 1;
        if (b[mid] <= x)
            res = mid, l = mid + 1;
        else
            r = mid - 1;
    }
    return res;
}
inline void sov()
{
    poread(n);
    t1.init(), t2.init();
    tot = 0;
    m = 0;
    for (register int i = 1, cnt = 0; i <= n; ++i)
    {
        poread(opt[i].opt), poread(opt[i].x);
        if (opt[i].opt == 0)
        {
            opt[i].y = opt[i].x + (++tot);
            po[tot] = i;
            b[++m] = opt[i].x, b[++m] = opt[i].y;
        }
    }
    sort(b + 1, b + m + 1);
    m = unique(b + 1, b + m + 1) - (b + 1);
    for (register int i = 1; i <= n; ++i)
    {
        if(opt[i].opt == 1)
            continue;
        opt[i].x = find(opt[i].x);
        opt[i].y = find(opt[i].y);
    }
    for (register int i = 1, cnt = 0; i <= n; ++i)
    {
        if (opt[i].opt == 0)
        {
            ++cnt;
            printf("%lld\n", t2.ask(opt[po[cnt]].y) - t1.ask(opt[po[cnt]].x - 1));
            t1.add(opt[po[cnt]].x), t2.add(opt[po[cnt]].y);
        }
        else
        {
            t1.del(opt[po[opt[i].x]].x), t2.del(opt[po[opt[i].x]].y);
        }
    }
}
} // namespace lky
signed main()
{
#ifdef lky233
    freopen("testdata.in", "r", stdin);
    freopen("testdata.out", "w", stdout);
#endif
    int T;
    poread(T);
    for (register int ttt = 1; ttt <= T; ++ttt)
    {
        printf("Case #%d:\n", ttt);
        lky::sov();
    }
}

T3 填数游戏

题意

给一个矩阵,每个格子可以填1或者-1,会给定一些已经填了的格子。问可能填法。

咋做


如果行列相加是偶数,没有可行解
否则判断给出的状态是否合法
合法的话答案就是\({2 ^ {(剩余格子 - 1)}}\)

//暴力判合法的,没有优化,时间贼差劲
#include <bits/stdc++.h>
using namespace std;
#define int long long
int MOD;
int n, m;
int k;
struct num
{
    int x, y, num;
} N[1000005];
int cx[1000006], cy[1000006];
inline int qpow(int a, int b)
{
    if (b <= 0)
        return 1;
    int res = 1;
    for (; b; b >>= 1)
    {
        if (b & 1)
            res = res * a % MOD;
        a = a * a % MOD;
    }
    return res;
}
signed main()
{
    cin >> n >> m;
    cin >> k;
    if ((n + m) & 1)
    {
        puts("0");
        return 0;
    }
    for (register int i = 1; i <= k; ++i)
    {
        cin >> N[i].x >> N[i].y >> N[i].num;
        ++cx[N[i].x], ++cy[N[i].y];
    }
    for (register int i = 1; i <= n; ++i)
    {
        if (cx[i] >= m)
        {
            int pan = 1;
            for (register int j = 1; j <= k; ++j)
            {
                if (N[j].x == i)
                    pan *= N[j].num;
            }
            if (pan == 1)
            {
                puts("0");
                return 0;
            }
        }
    }
    for (register int i = 1; i <= m; ++i)
    {
        if (cy[i] >= n)
        {
            int pan = 1;
            for (register int j = 1; j <= k; ++j)
            {
                if (N[j].y == i)
                    pan *= N[j].num;
            }
            if (pan == 1)
            {
                puts("0");
                return 0;
            }
        }
    }
    cin >> MOD;
    cout << qpow(2, (n - 1) * (m - 1) - k) << endl;
}
01-16 02:56