首页 > 其他分享 >2021 ICPC 沈阳站

2021 ICPC 沈阳站

时间:2022-11-01 14:46:29浏览次数:66  
标签:10 int dfs ICPC 2021 ans ind 沈阳站 find

Codeforces

B Bitwise Exclusive-OR Sequence

Description

给定 \(n\) 个数和 \(m\) 个限制:\(a_u\ a_v\ w\),要求 \(a_u\bigoplus a_v=w\)

求这 \(n\) 个数的和的最小值。

Solution

最初的想法:

​ 由于限制了\(0\leq w\leq 2^{30}\) ,所以考虑按位做。

​ 对于第 \(i\) 位: 遍历所有的限制条件 \(a_u\ a_v\ w\),如果 \(w\) 的第 \(i\) 位为 \(1\)(\((w>>i)\&1==1\)),则说明 \(a_u\) 和 \(a_v\) 的第\(i\) 位必须不同,给它俩连双向边。建完图之后对所有连通块染色,保证有边相连的两个点不同色,求出该位为 \(1\) 的最少数目 \(cnt\),将 \((1<<i)*cnt\) 累加进答案。

以上有一个很严重的错误(大错特错():对于一个限制条件 \(a_u\ a_v\ w\),它不仅限制了 \(a_u\) 和 \(a_v\) 在 \(w\) 为 \(1\) 的位上不同,也限制了 \(a_u\) 和 \(a_v\) 在 \(w\) 为 \(0\) 的位上相同。而上述思路不能保证其他位相同这个条件。考虑将限制该位相同的两数用并查集维护,将一个并查集内的点视作整体再考虑该位不同的限制,连边建图染色。

改进后:

​ 对于第 \(i\) 位: 第一遍遍历所有的限制条件 \(a_u\ a_v\ w\),如果 \(w\) 的第 \(i\) 位为 \(0\) ,则合并 \(a_u\ a_v\) 的并查集。第二遍遍历所有的限制条件 \(a_u\ a_v\ w\),如果 \(w\) 的第 \(i\) 位为 \(1\),则给二者所在的连通块之间连双向边。建完图之后进行黑白染色,累计答案。

Code

#include<iostream>
#include<vector>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
vector<int>to[N];
int n, m, fa[N], siz[N], col[N], tot1, tot0;
ll ans;
struct edge {
    int u, v, w;
}a[N << 1];
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int read()
{
    int x = 0, y = 1;char c = getchar();
    while (c < '0' || c>'9') { if (c == '-')y = -1;c = getchar(); }
    while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + c - '0';c = getchar(); }
    return x * y;
}
void init() {
    for (int i = 1;i <= n;++i)
        fa[i] = i, to[i].clear(), siz[i] = 0, col[i] = -1;
}
bool dfs(int u, int color) {
    if (col[u] != -1) {
        if (col[u] == color)
            return 1;
        return 0;
    }
    col[u] = color;
    if (color)
        tot1 += siz[u];
    else tot0 += siz [u];
    for (int i = 0;i < to[u].size();++i) {
        int v = to[u][i];
        if (!dfs(v, color ^ 1)) {
            return 0;
        }
    }
    return 1;
}
int main() {
    n = read(), m = read();
    for (int i = 1;i <= m;++i)
        a[i] = { read(),read(),read() };

    for (int i = 0;i < 30;++i){
        init();
        //STEP1:将限制该位相同的点合并并查集
        for (int j = 1;j <= m;++j)
            if ((a[j].w >> i) & 1 ^ 1) {  
                fa[find(a[j].u)] = find(a[j].v);
            }
        for (int i = 1;i <= n;++i)
            ++siz[find(i)]; //计算所有连通块的大小
        //STEP2:给限制该位不同的点所在的并查集连边
        for (int j = 1;j <= m;++j)
            if ((a[j].w >> i) & 1) {
                int u = a[j].u, v = a[j].v;
                if (find(u) == find(v)) {
                    printf("-1\n"); //如果二者在同一个并查集内,则不合法
                    return 0;
                }
                u = find(u), v = find(v);
                to[u].push_back(v);
                to[v].push_back(u);
            }
        //STEP3:进行黑白染色
        for (int j = 1;j <= n;++j)
            if (j == fa[j] && col[j] == -1 && to[j].size()) {
                tot1 = 0;
                tot0 = 0;
                if (!dfs(j, 0)){
                    printf("-1"); //不合法
                    return 0;
                }
                ans += (1ll << i) * min(tot0, tot1);
            }
    }
    printf("%lld\n", ans);
    return 0;
}

E Edward Gaming, the Champion

Description

给定一个字符串\(S\ (|S|\leq200000)\),求其中 \(edgnb\) 出现的次数。

Solution

主要是进行一个\(string\)函数的复习(?)

\(st.find(s,x)\) 会返回 \(st\) 字符串从下标 \(x\) 开始第一个出现的 \(s\) 字符串的首字符下标;

若找不到,则返回\(st.npos\)。

Code

#include<iostream>
using namespace std;
int main() {
	string s;
	cin >> s;
	int ind = 0, ans = 0;
	while ((ind = s.find("edgnb", ind)) != s.npos) {  //注意这里的括号
		++ans;
		++ind;
	}
	cout << ans << endl;
	return 0;
}

F Encoded Strings I

Sol

范围很小,直接暴力枚举模拟计算即可。

Code

#include<iostream>
using namespace std;
int n, num[25];
string ori_s, s, ans = "";
void calc() {
	int types = 0;
	for (int i = 0;i < 20;++i)
		num[i] = -1;
	for (int i = s.length() - 1;i >= 0;--i){
		int ind = s[i] - 'a';
		if (num[ind] == -1)
			num[ind] = types++;
	}
	for (int i = 0;i < s.length();++i)
		s[i] = num[s[i] - 'a'] + 'a';
	if (s > ans)
		ans = s;
}
int main() {
	cin >> n >> ori_s;
	for (int i = 1;i <= n;++i) {
		s = ori_s.substr(0, i);
		calc();
	}
	cout << ans << endl;
	return 0;
}

J Luggage Lock

Description

有一个四位密码锁,每一位取值范围为\([0,9]\)。

给定密码锁的初始状态 \(a_1a_2a_3a_4\) 和目标状态 \(b_1b_2b_3b_4\) 。

将对密码锁实施一步操作定义为:向上或向下同时转动连续的几位。(如 \(0000\) 可通过一步操作得到 \(0111\) 或 \(0990\) ,但无法得到 \(1001\))。

求从初始状态转到目标状态的最小操作次数。

Solution

首先,将 \(b_1b_2b_3b_4\) 减去 \(a_1a_2a_3a_4\) 得到 \(c_1c_2c_3c_4\) ,题目等价于将 \(c_1c_2c_3c_4\) 转到 \(0000\)。

注意到,对于每一位,只会朝一个方向转到指定数字,不存在先向上转几位再向下转几位得到目标数字的情况。

所以可以枚举每一位的旋转方向,对于每种情况计算最少步数。

我设定:负数只能一直做加法直到\(0\),正数只能一直做减法直到\(0\)。策略是:对于尽可能长连续的一段正数,共同减去这些数的最小值 \(x\) 并在答案累加上 \(x\) ,对于尽可能长连续的一段正数,共同加上这些数的最小的绝对值 \(x\),同样在答案上累加\(x\)。

注意对于\(0\)的处理!!有三种情况:一直不动;向上转\(10\)次;向下转\(10\)次。认为不转一定比转优的想法是错误的,比如\(-5\ 0\ -6\ -2\),若将 \(0\) 设定为 \(-10\),则答案为 \(10\) ;若对 \(0\) 不做任何操作,则答案为 \(11\)。

#include<iostream>
#define inf 2e9
using namespace std;
int read()
{
    int x = 0, y = 1;char c = getchar();
    while (c < '0' || c>'9') { if (c == '-')y = -1;c = getchar(); }
    while (c >= '0' && c <= '9') { x = (x << 1) + (x << 3) + c - '0';c = getchar(); }
    return x * y;
}
int ori[4], cur[4], tmp[4], ans = inf;
int tot;
int calc(int l, int r) {
    if (l > r)
        return 0;
    if (l == r)
        return abs(cur[l]);

    for (int i = l;i <= r;++i)
        if (cur[i] == 0)
            return calc(l, i - 1) + calc(i + 1, r);
    for (int i = l + 1;i <= r;++i)
        if (cur[i] * cur[i - 1] < 0)
            return calc(l, i - 1) + calc(i, r);

    int x = inf;
    for (int i = l;i <= r;++i)
        x = min(x, abs(cur[i]));
    for (int i = l;i <= r;++i) {
        if (cur[i] < 0)
            cur[i] += x;
        else
            cur[i] -= x;
    }
    return x + calc(l, r);
}
void dfs(int ind, bool type) {
    if (ind == 4) {
        for (int i = 0;i < 4;++i)
            tmp[i] = cur[i];
        ans = min(ans, calc(0, 3));
        for (int i = 0;i < 4;++i)
            cur[i] = tmp[i];
        return;
    }
    if (type)
        cur[ind] = ori[ind];
    else {
        if (ori[ind] < 0)
            cur[ind] = ori[ind] + 10;
        else if (ori[ind] > 0)
            cur[ind] = ori[ind] - 10;
        else {  //这里!!不要漏掉不讨论!!
            cur[ind] = 10;
            dfs(ind + 1, 0);
            dfs(ind + 1, 1);
            cur[ind] = -10;
            dfs(ind + 1, 0);
            dfs(ind + 1, 1);
        }
    }
    if (type || ori[ind] != 0) {
        dfs(ind + 1, 0);
        dfs(ind + 1, 1);
    }
}
int main() {
    int T = read();
    while (T--) {
        int x = read(), y = read();
        for (int i = 0;i < 4;++i) {
            int p1 = x % 10, p2 = y % 10;
            x /= 10, y /= 10;
            ori[i] = p2 - p1;
        }
        ans = inf;
        dfs(0, 1);
        dfs(0, 0);
        printf("%d\n", ans);
    }
    return 0;
}
/*
2
1389 3874
6086 0883
*/

标签:10,int,dfs,ICPC,2021,ans,ind,沈阳站,find
From: https://www.cnblogs.com/dttttttt/p/16847617.html

相关文章

  • 2021ICPC济南
    2021ICPC济南时隔一年再来补题,金牌题以下还是可以补的。C(组合数学,DP,贪心)题意两人每次从序列中取数字,希望自己拿到的数字和最大。求可行的操作序列方案数。思路看起来......
  • APACHE_CVE-2021-40438-SSRF漏洞分析复现
    影响版本v2.4.48及以下版本该版本中mod_proxy模块存在一处逻辑错误,导致攻击者可以控制反向代理服务器的地址,进而导致SSRF漏洞。该漏洞影响范围较广,危害较大,利用简单,目前......
  • The 2021 ICPC Asia Shenyang Regional Contest
    The2021ICPCAsiaShenyangRegionalContest我们按难易程度来,E,F<B,J<H,I,L,ME.EdwardGaming,theChampion直接输出edgnb子字符串数量。F.EncodedStringsI分......
  • 2021-10-22日记
    哎,真是醉了,今天真的是明白一件事儿,越是想贪便宜就越是贪不了便宜。说是今天信用卡有活动,加油中石化五折,于是下班后兴冲冲跑过去了,结果被告知有时间限制,过了晚上五点就没有这......
  • 校园社团活动管理系统 2021级大二期中考试
       大体上和2019级的基本相同,可以说是换汤不换药个人写的比较拙劣,基本完成了题目要求,发出来供大家参考,如有需要也可参考19级的第七次人口普查的代码 话不多说上......
  • NOI2021模拟测试赛(六十)
    题面西克把\(x\toy\)拆成\(x\tolca\toy\),而\(x\tolca\)的部分很好搞,不予阐述。关键是\(y\tolca\)的部分,我们考虑离线解决。从上往下走时,对每种颜色\(c\)......
  • 2021 ICPC EC Final B. Beautiful String 题解
    2021ICPCECFinalB.BeautifulString题解题意问给定字符串t的所有子串中形如"114514"分割方案之和。其中'1'、'4'、'5'表示某一字符串,且可重复。分析(暴力\(n^3\))......
  • NOIP 2021 游记
    Day0对着大纲找了点很板子的题做,主要找的dcc和scc缩点、树形DP和DP优化、KMP之类的。睡前祈祷不要失眠,结果在即将睡着后外面传来钢琴声,直接失眠........emmmmmmm。Day1......
  • CSP-S 2021 游记
    Day0下午试机,打了一下板子,发现编译都过不了人都傻了,结果是32位机。于是就以为整个机房都是32位机,考试的时候发现自己的机子是64位的,运气真好。试了一下对拍器,看来库里的......
  • LTSC 2021 CPU占用高、中文输入法不显示选字框的解决办法
    部分朋友在安装好LTSC2021后,可能会发现中文输入法不显示选字框,与此同时CPU占用很高的问题。这是因为在LTSC2021中,微软删除了Windows功能体验包的依赖组件,导致系......