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);
    }
    
  • 相关阅读:
    .net日期类与UNIX时间戳的相互转换,长数字
    钉钉的生日模块在哪
    js判断手机是苹果(IOS)还是安卓(android) H5手机端自适应宽高
    .net网站部署winserver2008R2 IIS只列出目录 浏览只显示目录浏览
    ajax有时请求不到数据 后台,有时收不到返回值的解决办法
    overflow不超出时不显示滚动条 属性解决内容未超出依然显示滚动条轨道的问题
    PB取datawindow生成的语句。要在datawindow的sqlpreview事件
    电脑C盘缓存路径在哪,清理C盘哪个文件夹可以删
    PB里执行写SQL语句
    SQL SERVER合并行。将多行数据合并成一行,字符串拼接
  • 原文地址:https://www.cnblogs.com/Alessandro/p/9796744.html
Copyright © 2011-2022 走看看