首页 > 其他分享 >20240419

20240419

时间:2024-04-19 23:33:26浏览次数:22  
标签:tmp int 1000005 ++ 20240419 pair include

T1

NFLSOJ P3581 No more xor problems, please!

实际上是异或和是最小公倍数的倍数。我们知道异或的结果二进制位数小于等于原来的。如果两个数没有倍数关系,则其最小公倍数一定不整除其异或和,因为最小公倍数的二进制位数至少多 \(1\)。所以合法的子集要么异或和为 \(0\),要么一个数是别的所有数的倍数,然后别的数的异或和为 \(0\)。求子集异或和为 \(0\) 的方案数可以使用线性基,设有 \(k\) 个数没有成功插入线性基,则答案就是 \(2^{k} - 1\),\(-1\) 是减去空集。因此我们把每个数的因子都拎出来扔到线性基里面,就可以算这个数为最大公约数时的答案。还有一些别的细节,都在代码里。

代码
#include <iostream>
#define int long long
using namespace std;
const int P = 998244353;
inline char nc(){
    static char buf[1000005],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000005,stdin),p1==p2)?EOF:*p1++;
}
inline int read() {
    int ret = 0;
    char c = nc();
    while (c < '0' || c > '9') c =nc();
    while ('0' <= c && c <= '9') ret = ret * 10 + c - 48, c = nc();
    return ret;
}
int n;
int a[1000005];
struct XOR_Linear_Base {
    int p[20], unsec;
    void Insert(int x) {
        for (int i = 19; ~i; i--) {
            if (x & (1 << i)) {
                if (p[i]) 
                    x ^= p[i];
                else {
                    p[i] = x;
                    return;
                }
            }
        }
        ++unsec;
    }
} p[1000005];
int qpow(int x, int y) {
    int ret = 1;
    while (y) {
        if (y & 1) 
            ret = ret * x % P;
        y >>= 1;
        x = x * x % P;
    }
    return ret;
}
int ap[1000005];
int ans = 1;
int fac[1000005], ifac[1000005], inv[1000005];
void Cpre(int n) {
    fac[1] = ifac[1] = inv[1] = fac[0] = ifac[0] = inv[0] = 1;
    for (int i = 2; i <= n; i++) {
        fac[i] = fac[i - 1] * i % P;
        inv[i] = (P - P / i) * inv[P % i] % P;
        ifac[i] = ifac[i - 1] * inv[i] % P;
    }
}
int C(int n, int m) { return (n < 0 || m < 0 || n < m ? 0 : (fac[n] * ifac[m] % P * ifac[n - m] % P)); }
signed main() {
    freopen("xor.in", "r", stdin);
    freopen("xor.out", "w", stdout);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        ap[a[i]]++;
        p[0].Insert(a[i]);
    }
    Cpre(n);
    ans = qpow(2, p[0].unsec);
    for (int i = 1; i <= n; i++) {
        if (ap[i]) {
            for (int j = 1; j <= ap[i]; j += 2) {
                ans += C(ap[i], j) * qpow(2, p[i].unsec) % P;
                ans >= P ? (ans -= P) : 0;
            }
            for (int j = 1; i * j <= n; j++) {
                if (ap[i * j]) {
                    for (int asdf = 1; asdf <= ap[i]; asdf++) 
                        p[i * j].Insert(i);
                }
            }
        }
    }
    cout << (ans + P - 1) % P << "\n";
    return 0;
}

T2

NFLSOJ P3580 Link Cut Tree

从叶子往上贪,设 \(f[i][0 / 1]\) 表示以 \(i\) 为根的子树,当前最上面一个连通块权值和为偶数 / 奇数时的最大块数,\(g[i][0 / 1]\) 表示最大块数时最上面一个连通块的权值最大多少。把儿子都转移过来之后如果奇偶性与 \(k\) 相同的那个权值可以消,就消掉。这样就是最优的。

代码
#include <iostream>
#define int long long
using namespace std;
const int inf = 214748647;
int n, k;
int head[1000005], nxt[2000005], to[2000005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
pair<int, int> f[1000005][2];
int a[1000005];
pair<int, int> operator+(pair<int, int> a, pair<int, int> b) { return make_pair(a.first + b.first, a.second + b.second); }
void dfs(int x, int fa) {
    for (int i = head[x]; i; i = nxt[i]) to[i] != fa ? dfs(to[i], x) : void();
    pair<int, int> tmp[2];
    tmp[0] = tmp[1] = f[x][0] = f[x][1] = make_pair(-inf, -inf);
    tmp[a[x] & 1] = make_pair(0, a[x]);
    int s = 0;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) {
            s += max(f[v][0].first, f[v][1].first);
            pair<int, int> x[2];
            x[0] = tmp[0], x[1] = tmp[1];
            tmp[0] = max(x[0] + f[v][0], x[1] + f[v][1]);
            tmp[1] = max(x[0] + f[v][1], x[1] + f[v][0]);
        }
    }
    tmp[0] = max(tmp[0], make_pair(s, 0ll));
    for (int i = 0; i < 2; i++) {
        f[x][i] = max(f[x][i], tmp[i]);
        if ((i == (k & 1)) && tmp[i].second >= k) 
            f[x][0] = max(f[x][0], make_pair(tmp[i].first + 1, 0ll));
    }
}
signed main() {
    freopen("linkcut.in", "r", stdin);
    freopen("linkcut.out", "w", stdout);
    int tc;
    cin >> tc;
    while (tc--) {
        cin >> n >> k;
        for (int i = 1; i <= n; i++) head[i] = 0, cin >> a[i];
        ecnt = 0;
        for (int i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            add(u, v);
            add(v, u);
        }
        dfs(1, 0);
        cout << max(f[1][0].first, f[1][1].first) << "\n";
    }
    return 0;
}

T3

NFLSOJ P12666 矩形

考虑容斥。拿总方案数减去不合法的。不合法的就是和为 \(3, 2, 1\)。和为 \(2, 1\) 时可以发现其中必有恰好两个点向外连一条 \(0\) 和一条 \(1\)。所以设每个点度数为 \(deg_i\),则这两种的总方案数就是 \(\frac{1}{2}\sum deg_i(n - 1 - deg_i)\)。求 \(deg\) 就考虑每个点有多少矩形与它不交。可以发现不交肯定是在左边或上面或右边或下面,或者在四个角上。先算出前四种的和,然后减去后四个角上的情况。接下来考虑和为 \(3\) 的情况。考虑扫描线,对每个三元组在其中最晚加入的矩形处算贡献。相当于一堆区间 \([l_i, r_i]\),问你有多少个二元组使得这个二元组中的区间都和给定区间 \([L, R]\) 有交且这两个区间有交。首先去掉与 \([L, R]\) 无交的,然后考虑拿总的减去这两个区间无交的,也就是 \(r_i < l_j\) 的 \(i, j\) 个数。开权值线段树维护左右端点,相当于求 \(\sum_{L \le i, j \le R} cntr_i \times cntl_j\)。这个东西类似于归并地做就可以了,应该是容易的。

代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <vector>
#include <map>
#define int long long
#define lowbit(x) ((x) & (-(x)))
using namespace std;
int n;
struct Rectangular {
    int l, r, d, u, deg;
} a[2000005];
struct BIT {
    int bit[4000005];
    void add(int x, int y) { for (; x <= 4000000; x += lowbit(x)) bit[x] += y; }
    int query(int x) {
        int ret = 0;
        for (; x; x -= lowbit(x)) ret += bit[x];
        return ret;
    }
    void clear() { memset(bit, 0, sizeof bit); }
} bit, bit2;
map<int, int> mp[2];
int d[2][2000005];
vector<int> vecl[4000005], vecr[4000005], vecd[4000005], vecu[4000005];
struct node {
    int cntl, cntr, val;
} T[4000005];
node operator+(node a, node b) {
    node c;
    c.cntl = a.cntl + b.cntl;
    c.cntr = a.cntr + b.cntr;
    c.val = a.val + b.val + a.cntr * b.cntl;
    return c;
}
struct Segment_Tree {
    void AddL(int o, int l, int r, int x, int v) {
        if (l == r) {
            T[o].cntl += v;
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid) 
            AddL(o << 1, l, mid, x, v);
        else 
            AddL(o << 1 | 1, mid + 1, r, x, v);
        T[o] = T[o << 1] + T[o << 1 | 1];
    }
    void AddR(int o, int l, int r, int x, int y) {
        if (l == r) {
            T[o].cntr += y;
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid) 
            AddR(o << 1, l, mid, x, y);
        else 
            AddR(o << 1 | 1, mid + 1, r, x, y);
        T[o] = T[o << 1] + T[o << 1 | 1];
    }
    node Query(int o, int l, int r, int L, int R) {
        if (L <= l && r <= R) 
            return T[o];
        int mid = (l + r) >> 1;
        if (R <= mid) 
            return Query(o << 1, l, mid, L, R);
        if (L > mid) 
            return Query(o << 1 | 1, mid + 1, r, L, R);
        return Query(o << 1, l, mid, L, R) + Query(o << 1 | 1, mid + 1, r, L, R);
    }
} seg;
signed main() {
    freopen("rect.in", "r", stdin);
    freopen("rect.out", "w", stdout);
    cin >> n;
    cin >> a[1].l;
    for (int i = 1; i <= n; i++) cin >> a[i].l >> a[i].r >> a[i].d >> a[i].u, d[0][++d[0][0]] = a[i].l, d[0][++d[0][0]] = a[i].r, d[1][++d[1][0]] = a[i].d, d[1][++d[1][0]] = a[i].u;
    sort(d[0] + 1, d[0] + d[0][0] + 1);
    sort(d[1] + 1, d[1] + d[1][0] + 1);
    for (int i = 1; i <= n * 2; i++) mp[0][d[0][i]] = (mp[0][d[0][i - 1]] + (d[0][i] != d[0][i - 1])), mp[1][d[1][i]] = (mp[1][d[1][i - 1]] + (d[1][i] != d[1][i - 1]));
    int X = mp[0][d[0][n * 2]], Y = mp[1][d[1][n * 2]];
    for (int i = 1; i <= n; i++) {
        vecl[a[i].l = mp[0][a[i].l]].emplace_back(i);
        vecr[a[i].r = mp[0][a[i].r]].emplace_back(i);
        vecd[a[i].d = mp[1][a[i].d]].emplace_back(i);
        vecu[a[i].u = mp[1][a[i].u]].emplace_back(i);
    }
    int cnt = 0;
    for (int i = 1; i <= X; i++) {
        for (auto v : vecl[i]) a[v].deg += cnt;
        cnt += vecr[i].size();
    }
    cnt = 0;
    for (int i = X; i; i--) {
        for (auto v : vecr[i]) a[v].deg += cnt;
        cnt += vecl[i].size();
    }
    cnt = 0;
    for (int i = 1; i <= Y; i++) {
        for (auto v : vecd[i]) a[v].deg += cnt;
        cnt += vecu[i].size();
    }
    cnt = 0;
    for (int i = Y; i; --i) {
        for (auto v : vecu[i]) a[v].deg += cnt;
        cnt += vecd[i].size();
    }
    for (int i = 1; i <= X; i++) {
        for (auto v : vecl[i]) a[v].deg -= (bit.query(Y) - bit.query(a[v].u));
        for (auto v : vecr[i]) bit.add(a[v].d, 1);
    }
    bit.clear();
    for (int i = X; i; i--) {
        for (auto v : vecr[i]) a[v].deg -= bit.query(a[v].d - 1);
        for (auto v : vecl[i]) bit.add(a[v].u, 1);
    }
    bit.clear();
    for (int i = 1; i <= X; i++) {
        for (auto v : vecl[i]) a[v].deg -= bit.query(a[v].d - 1);
        for (auto v : vecr[i]) bit.add(a[v].u, 1);
    }
    bit.clear();
    for (int i = X; i; i--) {
        for (auto v : vecr[i]) a[v].deg -= (bit.query(Y) - bit.query(a[v].u));
        for (auto v : vecl[i]) bit.add(a[v].d, 1);
    }
    bit.clear();
    int ans1 = 0;
    for (int i = 1; i <= n; i++) ans1 += a[i].deg * (n - 1 - a[i].deg);
    ans1 /= 2;
    int icnt = 0;
    int ans2 = 0;
    for (int i = 1; i <= X; i++) {
        for (auto v : vecl[i]) {
            int tmp = icnt - bit.query(a[v].d - 1) - (bit2.query(Y) - bit2.query(a[v].u));
            ans2 += tmp * (tmp - 1) / 2 - seg.Query(1, 1, Y, a[v].d, a[v].u).val;
            bit.add(a[v].u, 1), bit2.add(a[v].d, 1);
            seg.AddL(1, 1, Y, a[v].d, 1), seg.AddR(1, 1, Y, a[v].u, 1);
            ++icnt;
        }
        for (auto v : vecr[i]) {
            --icnt;
            bit.add(a[v].u, -1), bit2.add(a[v].d, -1);
            seg.AddL(1, 1, Y, a[v].d, -1), seg.AddR(1, 1, Y, a[v].u, -1);
        }
    }
    cout << n * (n - 1) * (n - 2) / 6 - ans1 - ans2 << "\n";
    return 0;
}
</details>

---

线性基求子集异或和为 $0$ 的方案数。

大力容斥,分讨。

标签:tmp,int,1000005,++,20240419,pair,include
From: https://www.cnblogs.com/forgotmyhandle/p/18147005

相关文章