zoukankan      html  css  js  c++  java
  • 牛客 | 一起来做题~欢乐赛2 (AK 题解)

    补题链接:Here

    A.新比赛,在眼前。

    对于每次猜数和裁判的判断,可以确定一个区间内所有的数都有可能,比如对于样例中(8 +)来说,[ -INT_MIN, 7] 中所有的数都有可能,那么对于每次猜数,我们可以将一个区间内所有的数都加上1,这就用到差分了,由于区间端点在这里的作用与区间内的数相同,所以我们不需要开那么大的数组(实际上也开不了),用一个map就行了。

    const int N = 1e5 + 10, inf = INT_MAX;
    int n;
    map<int, int>mp;
    void solve() {
        cin >> n;
        int x; char ch;
        while (n--) {
            cin >> x >> ch;
            if (ch == '.')mp[x]++, mp[x + 1]--;
            else if (ch == '+')mp[-inf]++, mp[x]--;
            else mp[x + 1]++;
        }
        int ans = 0, cnt = 0;
        for (auto p : mp) {
            cnt += p.second;
            ans = max(ans, cnt);
        }
        cout << ans << "
    ";
    }
    

    B.有人说,上周题还没做完……

    虽然写出了状态转移方程,但不太会证明,这里引用一下 RingweEH 的证明过程

    有一个很显然的想法:答案中的系统方案一定是由原先的系统方案去掉若干种货币得到

    事实上这就是正确的。

    Proof

    令给出的系统中的货币面值为 A 集合,需要得到的货币面值为 B 集合。

    引理:A 集合中不能被其他数组成的数一定会在 B 集合中出现。

    引理的证明:设有一个数 x∈A 且不能被 A 集合中其他数凑出来。 根据等价,如果 x∉B ,那么 B 中的其他数一定能组成 x .这就说明 B 中至少存在一个不属于 A 集合且不能被 A 组合出来的数(不然 A 集合就一定能合成 x ),那么这个数本身不属于 A 能组成的范畴,却属于 B 能组成的范畴,就不符合题意了。所以 x∈B ,引理正确性证毕。

    那么现在我们需要证明:B⊆A .

    仍然采用反证法。设存在一个数 x 满足 x∈B 且 x∉A.

    根据题意,显然 x 能被 A 中若干个 a1,a2,…,ak 组成(假定这些数不能被拆分成 A 中其他的数,如果能拆分就直接拿拆分方案替换即可)。根据引理,这些数都属于 B ,也就是说,B 完全可以通过这些数组成 x ,那么 B 中再存在一个 x 显然就是多余的,和 B 集合最小的要求不符。

    Q.E.D.

    接下来的事情就非常简单了。我们只需要考虑 A 集合中哪些数是多余的就好了。

    题目暗示:现在网友们打算 简化 一下货币系统。这说明就是在原基础上去掉某些数(

    这个事情可以一次 DP 解决。观察到 (a[i]) 的范围只有 (25000) ,那么可以直接设 (f[i]) 表示 (i) 这个数能否被前面已经出现过的 (a[j]) 组成。

    • 如果枚举到 (a[i]) 时,(f[a[i]] = 1) ,那么直接计入答案并跳过即可;
    • 如果没有,那么枚举所有的 (j = a[i] ~ m)(f[j] |= f[j - a[i]]) (就是用 (j−a[i])(a[i]) 组成 (j) ,枚举范围中的 (mx) 表示所有 (a[i]) 中的最大值)
    const int N = 110, M = 25010;
    int a[N], f[M];
    int n;
    void solve() {
        cin >> n;
        for (int i =  1; i <= n; ++i)cin >> a[i];
        sort(a + 1, a + 1 + n);
        int mx = a[n];
        memset(f, 0, sizeof(f)), f[0] = 1;
        int ans = n;
        for (int i = 1; i <= n; ++i) {
            if (f[a[i]]) {ans--; continue;}
            for (int j = a[i]; j <= mx; ++j)
                f[j] |= f[j - a[i]];
        }
        cout << ans << "
    ";
    }
    

    C.要变强,不空喊,ak题目,继续向前。

    这个问题的难点在于如何统计出所有和可能出现的情况,并且不能重复。
    很容易想到用桶去存储每一个数,即某个和能够组合出来则为1,否则为0

    不妨令 (dp[i][j]) 表示为第 (i) 次选择时,和为 (j) 的情况是否出现过

    但是内存方面需要 (1e8)(int) 内存,显然是不可接受的

    那么我们考虑用 (bitset) 优化内存,由递推方程:(dp_i = dp_{i}|=(dp_{i-1}<<(j*j)))

    代表第 (i) 次选择的时候是否能从当前状态转移到和为(j) 的状态

    写完状态转移方程发现 (dp_i) 仅与 (dp_{i - 1}) 有关系,所以由滚动数组来节省空间

    bitset<1000010>now, nxt;
    void solve() {
    	int n;
    	cin >> n;
    	now[0] = 1;
    	for (int i = 1; i <= n; ++i) {
    		int l , r; cin >> l >> r;
    		for (int j = l; j <= r; ++j)
    			if (j == l) nxt = (now << (j * j));
    			else nxt |= (now << (j * j));
    		now = nxt;
    	}
    	cout << now.count() << "
    ";
    }
    

    D.kuangbin巨巨曾言:人一我百,人十我万。

    这个图必须要连通的,其次要有个奇环
    对于不连通的图,只需要加上 连通块数量-1 的边即可将它变成连通
    对于不存在奇环的,可以在连通的图上加一条边生成一个奇环
    所以 (DFS) 每个连通块,在 (DFS) 过程中顺便用染色法判定下这是不是个二分图
    因为二分图不含奇环,如果它是二分图就说明它有奇环
    最后答案就是 连通快的数量 - (有无奇环)

    const int N = 1e5 + 10;
    int n, m, odd, x, y, res, vis[N], color[N];
    vector<int> g[N];
    void dfs(int u) {
    	for (auto v : g[u]) {
    		if (!vis[v]) {
    			vis[v] = 1;
    			color[v] = !color[u];
    			dfs(v);
    		} else if (color[u] == color[v])
    			odd = 1;
    	}
    }
    void solve() {
    	cin >> n >> m;
    	for (int i = 1; i <= m; i++) {
    		cin >> x >> y;
    		g[x].push_back(y);
    		g[y].push_back(x);
    	}
    	for (int i = 1; i <= n; i++) {
    		if (!vis[i]) {
    			res++;
    			vis[i] = color[i] = 1;
    			dfs(i);
    		}
    	}
    	cout << res - odd << '
    ';
    }
    

    E.暂且莫放豪言要刷千万题,先把每场比赛好好练!

    二分图左侧为属性值,右侧为N个装备
    每个装备向其两个属性值连边(反向),用匈牙利从小到大枚举属性值,直到无法匹配为止
    因为在二分图上每次增广时不会使得之前的匹配点变为非匹配点,所以正确性显然能保证
    注意要使用时间戳优化匈牙利,不然memset的开销过大

    const int N = 1e4 + 10, M = 1e6 + 10;
    int n, Tim;
    vector<int>e[N];;
    int vis[M], mat[M];
    bool find(int u) {
    	for (auto v : e[u]) {
    		if (vis[v] != Tim) {
    			vis[v] = Tim;
    			if (!mat[v] || find(mat[v])) {
    				mat[v] = u; return true;
    			}
    		}
    	}
    	return false;
    }
    void solve() {
    	int n; cin >> n;
    	for (int i = 1, u, v; i <= n; ++i) {
    		cin >> u >> v;
    		e[u].push_back(i);
    		e[v].push_back(i);
    	}
    	for (int i = 1; i <= N; ++i) {
    		++Tim;
    		if (!find(i)) {
    			cout << i - 1;
    			return ;
    		}
    	}
    }
    
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    postman: 字符串与数字的转换
    postman:截取字符串字符
    postman:获取txt变量中数据
    postman:参数化外部文件txt、csv、json
    postman:全局变量、环境变量、collection模块变量
    windows下更新python报错permission denied
    vertica 7.0 使用kafka
    Kafka报错-as it has seen zxid 0x83808 our last zxid is 0x0 client must try another server
    vertica 8.0 新特性
    mysql source导入报错ERROR 1366的解决方法
  • 原文地址:https://www.cnblogs.com/RioTian/p/14810432.html
Copyright © 2011-2022 走看看