zoukankan      html  css  js  c++  java
  • 牛客网提高五练习

    吉老师的题,出的很有水平(应该是T1,T2难度)

    同余方程

    题意

    给出 (l1, l2, r1, r2, m) 询问 (displaystyle sum_{i = l1}^{r1} sum_{j = l2}^{r2} [m | i oplus j])

    分析

    对于前 (30) 分,我们可以简单枚举

    对于第二个点,我们已知一个数

    显然对于 (10 ^ {18}) 的数据范围,我们只能考虑 (O(1)) 或者 (O(logn)) 的算法

    由于如果需要枚举 ([l, r]) 中的数,时间复杂度将会乘上 (O(n)),我们需要考虑直接进行计算

    因为确定了 (x) ,不同 (y) 的值与 (x) 异或的值一定不一样,并且一定是紧密排布的

    因此我们可以直接计算出这些数的开头以及结尾,还有 (m) 的倍数的个数

    对于所有数据

    我们考虑将这个问题转换为前缀问题

    (f(x, y))(displaystyle sum_{i = 0}^{x} sum_{j = 0}^{y} [m | i oplus j])

    那么我们可以将原问题转换为 (f(r1, r2) - f(l1, r2) - f(r1, l2) + f(l1, l2))

    (f(x, y)) 可以通过二进制分解后选取一部分做上一种情况来求解

    代码

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    #define qword long long
    
    qword l1, l2, r1, r2, p, ans = 0;
    #define M 998244353LL
    
    qword solve(qword l, qword r, qword p) {
        if (l % p) l = (l / p + 1) * p;
        if (r % p) r = (r / p) * p;
        if (l > r) return 0;
        return (r / p - l / p + 1) % M;
    }
    qword solve(qword a, qword b){
        qword res = 0;
        for (qword i = a; i > 0; i -= i & -i)
            for (qword j = b; j > 0; j -= j & -j){
                qword m = min(i & -i, j & -j);
                qword n = max(i & -i, j & -j);
                qword x = ((i - (i & -i)) ^ (j - (j & -j))) | (n - 1);
                qword y = x - n + 1;
                (res += m % M * solve(y, x, p) % M) %= M;
            }
        return res;
    }
    
    int main() {
    	cin >> l1 >> r1 >> l2 >> r2 >> p;
    	if (r1 <= 5000 && r2 <= 5000) {
    		for (qword i = l1; i <= r1; ++ i)
    			for (qword j = l2; j <= r2; ++ j) {
    				qword res = i ^ j;
    				if (res % p == 0) ans ++;
    				if (ans > M) ans -= M;
    			}
    	}
    	else ans = solve(++r1, ++r2) - solve(l1, r2) - solve(r1, l2) + solve(l1, l2);
    	cout << (ans + M) % M << endl;
    }
    

    旅行

    题意

    每条边至少走一次,从 (1) 出发回到 (1)

    解析

    应为这是一个闭合回路,如果我们将重复走的边进行拆分,那这张图就是个欧拉回路

    因此,我们要保证每个点的度都是偶数

    考虑添加哪些边使得每个点的度是偶数

    由于每加一条边,只会改变两个点的度,并且这个度是可以传递的 最短路 + dp? 时间暴了

    发现由于边权全部是 (2) 的幂次,并且幂次高的一定比所有幂次小的的和大,所以我们只要选取了高幂次的,我们就可以通过选取低幂次来降低总的 (ans)

    由此,我们可以发现我们最终选取的值全部都在这个图的 MST

    那么原问题将转变成选取那些边,可以使得这棵树上所有的点的权值全是偶数

    简单DFS即可

    注意取模

    代码

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    #define qword long long
    
    const int maxn = 5e5 + 10;
    
    int n, m, u[maxn], v[maxn], w[maxn], fa[maxn], d[maxn], ver[maxn << 1], head[maxn << 1], Next[maxn << 1];
    long long mi[maxn], tot = 0, ans = 0, edge[maxn << 1];
    bool vis[maxn];
    
    #define CH ch = getchar()
    
    const int q = 998244353;
    
    template <class T> inline void read(T &x) {
    	x = 0; int ch, f = 0;
    	for (CH; ch < '0' || ch > '9'; CH) if (ch == '-') f = 1;
    	for (; ch >= '0' && ch <= '9'; CH) x = (x << 1) + (x << 3) + (ch ^ 48);
    	if (f) x = -x;
    }
    
    int find(int x) {
    	return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    
    inline void addEdge(int x, int y, int z) {
    	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
    }
    
    int dfs(int x) {
    	vis[x] = 1;
    	for (int i = head[x]; i; i = Next[i]) {
    		int y = ver[i], z = edge[i];
    		if (vis[y]) continue;
    		int isAdd = dfs(y);
    		if (isAdd) d[y] ++, d[x] ++, ans += z, ans %= q;
    	}
    	if (d[x] & 1) return 1;
    	else return 0;
    }
    
    int main() {
    	read(n), read(m); mi[0] = 1;
    	for (int i = 1; i <= m; ++ i) {
    		read(u[i]), read(v[i]); fa[i] = i;
    		d[u[i]] ++, d[v[i]] ++; mi[i] = (mi[i - 1] << 1) % q;
    		ans += mi[i]; ans %= q;
    	}
    	for (int i = 1; i <= m; ++ i) {
    		int uu = u[i], vv = v[i]; // ww -> 2 ^ i
    		int x = find(uu), y = find(vv);
    		if (x != y) addEdge(uu, vv, mi[i]), addEdge(vv, uu, mi[i]), fa[x] = y;
    	}
    	dfs(1);
    	cout << ans << endl;
    }
    

    串串

    题意

    如题所述

    解析

    (T) 是从 (S) 中删除一些字符得到的,(T) 一共有 (inom{c+d}{c})

    考虑逆过程,我们在 (T) 上面插入 (a-c)(0)(b-d)(1) 来得到 (S)

    但是直接插入会计算重复,比如 (T)(000),插入一个 (0)(4) 种位置,但是得 到的都是相同的结果

    为了避免计算重复,我们限制所有统计的插入方案,必须对应着 (T)(S) 中最 靠前的一次出现,比如在上面的例子中,我们只统计 (0) 插入到最末尾的情况。

    显然在这样的要求下, (0) 只能插入到 (1) 前面或者末尾,(1) 只能插入到 (0) 前面或者末尾。因此在一共 (c+d+1) 个间隔中,除了末尾以外,其他间隔都只能插入一种数字

    可以枚举末尾有几个 (0) 几个 (1),然后用插板法统计方案

    代码

    #include <cstdio>
    
    #define qword long long
    const int p = 1e9 + 7;
    const int maxn = 2010;
    int ans, C[maxn << 1][maxn], a, b, c, d;
    
    int main() {
        scanf("%d %d %d %d",&a, &b, &c, &d);
        C[0][0] = 1;
        for (int i = 1; i <= a + b; ++ i){
            C[i][0] = 1;
            for (int j = 1; j <= i; ++ j)
                C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % p;
        }
        if(!c || !d){
            printf("%lld
    ", C[a + b][a]);
            return 0;
        }
        for (int i = 0; i <= a - c; ++ i)
            for (int j = 0; j <= b - d; ++ j){
                qword ret = C[i + j][j];
                (ret *= C[c + d][d]) %= p;
                int cq0 = a - c - i, cq1 = b - d - j;
                (ret *= C[cq1 + c - 1][c - 1]) %= p;
                (ret *= C[cq0 + d - 1][d - 1]) %= p;
                (ans += ret ) %= p;
            }
    	printf("%d", ans);
    }
    
  • 相关阅读:
    PAT (Advanced Level) Practice 1071 Speech Patterns (25分)
    PAT (Advanced Level) Practice 1070 Mooncake (25分)
    PAT (Advanced Level) Practice 1069 The Black Hole of Numbers (20分)
    PAT (Advanced Level) Practice 1074 Reversing Linked List (25分)
    PAT (Advanced Level) Practice 1073 Scientific Notation (20分)
    第一次冲刺个人总结01
    构建之法阅读笔记01
    人月神话阅读笔记01
    四则运算2
    学习进度条(软件工程概论1-8周)
  • 原文地址:https://www.cnblogs.com/Alessandro/p/9796744.html
Copyright © 2011-2022 走看看