Logo FiraCode 的博客

博客

标签
暂无

CF165Etj

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-07-30 11:27:43

题解思路:

他让我们找到一个 $a_i & a_j = 0$。 那么对于 $a_i = 1$ 那么 $a_j = 0$。 若 $a_i = 0$ 那么 $a_j = 0$ 或者 $a_j = 1$。 那么 $a_j$ 必须是 ~$a_i$ 的子集,其中 ~ 表示取反。 那么我们就是要求 $a_j & i = a_j$ 并且 $j$ 最小。 那么我们令 $f_{a_j} = j$。 然后我们找到一个 $g_i = \min_{i & j = j}\{f_j\}$。 那么这个就很像字集和问题,只要把字集和换成求最小值就可以了。

AC CODE:

#include <bits\/stdc++.h>
using namespace std;
typedef long long ll;
const int N = (1 << 22) + 10;
const int M = 22;
int n;
int f[N], a[N];
int main() {
	scanf("%d", &n);
	for (int i = 0; i < (1 << M); ++i)
		f[i] = n + 1;
	for (int i = 1; i <= n; ++i) {
		int x;
		scanf("%d", &x);
		a[i] = x;
		f[x] = min(f[x], i);
	}
	for (int i = 0; i < M; ++i)
		for (int j = 0; j < (1 << M); ++j) {
			if (j & (1 << i))
				f[j] = min(f[j], f[j - (1 << i)]);
		}
	for (int i = 1; i <= n; ++i) {
		int x = (1 << M) - 1 - a[i];
		printf("%d%c", (f[x] > n) ? -1 : a[f[x]], " \n"[i == n]);
	}
	return 0;
}

CF369A

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-09-28 11:15:25

题意:

给你一些盘子和碗,每天有两种吃法,一种要一个碗,一种碗也可以盘子的可以,用完之后碗或者盘子就脏了,然后每次用的要是干净的,给你碗和盘子的数量 $m,k$,求最少洗碗的次数。

题解思路:

贪心:

对于碗不干净的数量记为 $x$,把盘子不干净的数量记为 $y$,那么当第一种就分两种情况,若 $x = m$ 则答案加一,否则就让 $x$ 加一,若是第二种就看看 $y = k$ 若成立且 $x = m$ 那么答案加一,否则若 $y < k$ 那么 $y+1$ 否则 $x + 1$。

CODE

CF1051C

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-09-29 11:11:18

题意:

给你 $n$ 个数,一个数是好的当且仅当它只出现了一次,给你一个序列,求是否可以把这个序列分成两组,是的他们好的的数字相同。

题解思路:

我们先排序,然后统计每个数字出现的次数,设 $t1$ 为出现 $1$ 次的数的个数,$t2$ 为出现两次的数的个数,$tn$ 表示出现了大于 $2$ 的数的个数。

若他出现以此的数的个数是奇数,并且没有大于 $2$ 的数的个数,那么就无解,输出 NO

否则一定有解输出 YES

然后我们分两种况来讨论,第一种情况是 $t1$ 是偶数,那么若这个块的长度是 $1$ 那么我们用一个 $cnt$ 来计数,若 cnt == 1 则把他分给 A 否则分给 B,然后 cnt = 1 - cnt

否则都给他分给 A 或者 B

那么若 $t1$ 是奇数,那么若这个块的长度是 $1$ 那么我们用一个 $cnt$ 来计数,若 cnt == 1 则把他分给 A 否则分给 B,然后 cnt = 1 - cnt

若他的长度是 2 就直接都分给 AB

否则若是第一次长度 $\ge 3$,若当前的 cnt == 0 则第一个就分给 B 否则分给 A,然后后面的数字都分给 BA(若上一次选了 A 那么后面就该给 B 反之亦然)。

然后 cnt = 1 - cnt

若他不是第一次长度 $\ge 3$ 就把他都给 A 或者都给 B

CODE:

又臭又长的代码

#include <bits\/stdc++.h>
using namespace std;
const int N = 110;
pair<int, int> a[N];
int n;
char ans[N];
int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i].first;
        a[i].second = i;
    }
    sort(a + 1, a + 1 + n);
    int t1 = 0, t2 = 0, tn = 0;
    for (int i = 1; i <= n;) {
        int t = a[i].first;
        int sz = 0;
        int temp = i;
        while (a[i].first == t) {
            i ++;
            sz++;
        }
        if (sz == 1) {
            t1 ++;
        }
        else if (sz == 2) {        
            t2 ++;
        }else {
            tn ++;
        }
    }
    if (t1 & 1) {
        if (!tn) {
            puts("NO");
            return 0;
        }
    }
    puts("YES");
    if (t1 & 1) {
        int cnt = 0;
        bool ok = false;
        for (int i = 1; i <= n;) {
            int sz = 0;
            int t = a[i].first;
            int id = a[i].second;
            int ttemp = i;
            while(a[i].first == t)
                i ++, sz ++;
            if (sz == 1) {
                if (cnt == 0)
                    ans[id] = 'B';
                else
                    ans[id] = 'A';
                cnt = 1 - cnt;
            }else if (sz == 2) {
                ans[id] = ans[a[ttemp + 1].second] = 'A';
            }else {
                if (!ok) {
                    char c = cnt == 0 ? 'B' : 'A';
                    ans[id] = c;
                    for (int j = 1; j < sz; ++j)
                        ans[a[ttemp + j].second] = c == 'A' ? 'B' : 'A';
                    cnt = 1 - cnt;
                    ok = true;
                }else {
                    for (int j = 1; j <= sz; ++j)
                        ans[a[ttemp + j - 1].second] = 'B';
                }
            }
        }
    }else {
        int cnt = 0;
        bool ok = false;
        for (int i = 1; i <= n;) {
            int sz = 0;
            int t = a[i].first;
            int id = a[i].second;
            int ttemp = i;
            while(a[i].first == t)
                i ++, sz ++;
            if (sz == 1) {
                if (cnt == 0)
                    ans[id] = 'B';
                else
                    ans[id] = 'A';
                cnt = 1 - cnt;
            }else if (sz == 2) {
                ans[id] = ans[a[ttemp + 1].second] = 'A';
            }else {
                for (int j = 1; j <= sz; ++j)
                    ans[a[ttemp + j - 1].second] = 'B';
            }
        }    
    }
    for (int i = 1; i <= n; ++i)
        cout << ans[i];
    return 0;
}

CF27C

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-10-01 18:36:42

题意:

现在,给定一个 $n$ 个整数的序列 $a_1,a_2,...,a_n$。

请你找到该序列的最短非有序子序列。

题解思路:

若他是非有序,那么就是有两种请况:

  1. 先上升,再下降
  2. 先下降,再上升

为什么没有相同的呢?

因为我们要求的是最短的子序列,所以有相等的我们只留一个就可以了。

而且我们发现这两种情况只需要三个元素就够了。

那么我们其实就是要求在这个序列里是否有三个元素满足:

第一个和第三个都大于或都小于中间的元素。

那么对于第一种情况,我们可以枚举中间的元素,那么我们只要判断是否有两个元素,满足一个在他左边一个在他右边并且两个数都比他小。

对于第二种请况同理,只是要求两边的元素都比他大就可以了。

那么我们就预处理 $minn1_i,minn2_i,maxn1_i,maxn2_i$ 分别代表他前 $i$ 个的最小值的下标,后 $i$ 个的最小值的下标,前 $i$ 个的最大值的下标和后 $i$ 个的最大值的下标。

那么对于一个数,我们就判断他前面和后面的最大(最小)值是否满足条件即可。

CODE:

#include <bits\/stdc++.h>
using namespace std;
int n;
int a[100010];
int minn1[100010], maxn1[100010], minn2[100010], maxn2[100010];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    if (n < 3) {\/\/长度小于三就一定不行
        puts("0");
        return 0;
    }
    maxn1[1] = minn1[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (a[maxn1[i - 1]] > a[i])\/\/若他是比前一个小,那么下标还是前i-1的最大值
            maxn1[i] = maxn1[i - 1];
        else
            maxn1[i] = i;\/\/否则赋值成他自己
        if (a[minn1[i - 1]] < a[i])\/\/最小值同理
            minn1[i] = minn1[i - 1];
        else
            minn1[i] = i;
    }
    \/\/从后面开始的同理
    maxn2[n] = minn2[n] = n;
    for (int i = n - 1; i >= 1; --i) {
        if (a[maxn2[i + 1]] > a[i])
            maxn2[i] = maxn2[i + 1];
        else
            maxn2[i] = i;
        if (a[minn2[i + 1]] < a[i])
            minn2[i] = minn2[i + 1];
        else
            minn2[i] = i;
    }
    int i;
    for (i = 2; i < n; ++i) {\/\/枚举中间值
        if(a[minn1[i - 1]] < a[i] && a[minn2[i + 1]] < a[i]) {\/\/第一种情况
            puts("3");
            printf("%d %d %d\n", minn1[i - 1], i, minn2[i + 1]);
            break;
        }
        if (a[maxn1[i - 1]] > a[i] && a[maxn2[i + 1]] > a[i]) {\/\/第二种请况
            puts("3");
            printf("%d %d %d\n", maxn1[i - 1], i, maxn2[i + 1]);
            break;
        }
    }
    if (i == n) {
        puts("0");
    }
    return 0;
}

CF982C

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-10-02 08:29:02

题解思路:

当节点数是奇数的时候,连通块内一定会有一个是奇数个点,所以一定无解。

那么节点数是偶数的时候,我们对于每条边来说,当他连着的部分有奇数个点,那么这条边一定不能删,若都是偶数个点,那么就要删掉。

那么我们就一遍 dfs 判断他们两边的点的个数来判断删或不删就可以了。

AC CODE:

#include <bits\/stdc++.h>
using namespace std;
const int N = 100010, M = N * 2;
int n;
int ans;\/\/表示一共可以删除多少条边
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u, int fa) {
	int sz = 1;
	for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];
		if (j != fa) {
			int s = dfs(j, u);
			if (!(s & 1)) ans++;\/\/是偶数个点,那么就可以删
			sz += s;\/\/统计长度
		}
	}
	return sz;
}
int main() {
	scanf("%d", &n);
	if (n & 1) {\/\/无解
		puts("-1");
		return 0;
	}
	memset(h, -1, sizeof(h));
	for (int i = 2; i <= n; ++i) {
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
	}
	dfs(1, -1);\/\/dfs
	printf("%d\n", ans);
	return 0;
}

CF977E

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-10-02 09:53:08

题解思路:

我们先用并查集来维护连通块,然后再判断每个联通块是否是环即可。

那么判断是否是环,我们就看看他每个点的度数是否都为 $2$ 就可以了。

那么再读入边的时候我们就维护一下并查集还有每个点的度数,那么当一个点的度数不为 $2$ 的时候,就把他所在的集合标记一下。

然后最后遍历一下所有的集合,看看是否标记就可以了。

AC CODE:

#include <bits\/stdc++.h>
using namespace std;
int n, m;
int p[200010];
int du[200010];
bool st[200010];
int find(int x) {return x == p[x] ? x : p[x] = find(p[x]);}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)\/\/初始化并查集
		p[i] = i;
	while (m--) {
		int a, b;
		scanf("%d%d", &a, &b);
		if (find(a) != find(b))
			p[find(a)] = find(b);\/\/预处理并查集
		du[a] ++; du[b] ++;\/\/度数+1
	}
	for (int i = 1; i <= n; ++i) {
		if (du[i] != 2) {\/\/不满足条件
			st[find(i)] = true;\/\/标记一下
		}
	}
	int res = 0;
	for (int i = 1; i <= n; ++i)
		if (p[i] == i && !st[i])\/\/是环
			res ++;\/\/答案++
	printf("%d\n", res);
	return 0;
}

CF1485D题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-04-30 10:13:33

题解思路:

直接构造确实很难,我们不妨这样想:

我们对一个 $n$ 行 $m$ 列的矩阵,对他黑白染色,那么我们发现一个黑点他上下左右都是白点,然后我们想办法让黑点是相同的值。

那么我们就可以取这 $1$ ~ $16$ 的 $\operatorname{lcm}$ 就可以了,因为 $1 \le a_{i,j} \le 16$ 所以他们的 $\operatorname{lcm}$ 就不是很大。

那么黑色的数构造好了,就差白色点的。

那么白色点就可以: $$a_{i,j} = \operatorname{lcm} - a_{i , j} ^ 4$$

AC CODE:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
int gcd (int a , int b) {
	return b == 0 ? a : gcd (b , a % b);
}
ll res[20];
int main() {
	ll lcm = 1;
	for (int i = 1; i <= 16; ++ i) {\/\/算lcm,也可以算出来以后直接用就可以了
		ll d = gcd (i , lcm);
		lcm = i \/ d * lcm;
	}
	for (int i = 1; i <= 20; ++ i) {\/\/打表
		ll x = 1ll * i * i * i * i;\/\/i ^ 4
		ll now = lcm + x;
		for (int j = 1; j <= 16; ++ i) 
			if (now % j == 0) 
				res[j] = now;\/\/算白点的值
	}
	int n , m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++ i) {
		for (int j = 1; j <= m; ++ j) {
			int x;
			cin >> x;
			if ((i + j) & 1) cout << lcm << ' ';\/\/如果是黑点,那么就输出lcm
			else cout << res[x] << ' ';\/\/否则就输出白点的值
		}
		cout << endl;\/\/输出换行
	}
	return 0;\/\/完结散花
}

码字不易,求赞!

CF1485E题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-04-30 12:15:59

题解思路:

设 $dp_{i,j}$ 代表:$i \to $ 蓝,$j \to $ 红,能到达状态最大值。

我们再枚举一个 $dp_{x , y}$,然后转移,时间复杂度就是 $\mathcal O(n ^ 4)$。

我们可以加入一些优化:

$dp_{x , y}$: 要么从 $dp_{i , fa_{y}}$ 转移来。

要么从 $dp_{i , fa_{x}}$ 转移来。

这样的话就不用枚举 $j$ 了,时间复杂度变成了 $\mathcal O(n ^ 3)$。

我们发现直接一维就可以了,因为蓝色这个点可以在任意位置,所以我们只要知道层数就可以了,而红色点和蓝色点在同一层,所以只保留红色点那一维就可以了。

装态转移方程:

$1$ 操作

$dp_{y} = \max (dp_{y}, dp_{fa_{y}} + \operatorname{abs} (a_{y} - mi))$ 其中 $mi$ 表示这一层的最小值。

$dp_{y} = \max (dp_{y}, dp_{fa_{y}} + \operatorname{abs} (a_{y} - ma))$ 其中 $ma$ 表示这一层的最大值。

$2$ 操作

$dp_{y} = \max (dp_{y} + dp_{fa_{x}} + \operatorname{abs} (a_{x} - a_{y}))$。

优化完之后时间复杂度就变成了 $\mathcal O(n ^ 2)$。

我们在做一个优化,这个优化只能优化二式:

先把绝对值去掉:

要么是:$dp_{y} = \max (dp_{y}, dp_{fa_{x}} + a_{y} - a_{x})$。

要么是:$dp_{y} = \max (dp_{y}, dp_{fa_{x}} + a_{x} - a_{y})$。

再把 $dp_{fa_{x}}$ 给提出来,那么我们只需要关心两个值就可以了,我们定义两个值:

$f(x) = dp_{fa_{x}} - a_{x}$

$g(x) = dp_{fa_{x}} + a_{x}$

那么我们就只需要记录 $f(x),g(x)$ 的最大值就可以了,我们就在转移时直接用就可以了。

状态转移变为:

$dp_{y} = \max (dp_{y}, a_{y} + \max{f(x)})$。

$dp_{y} = \max (dp_{y}, - a_{y} + \max{g(x)})$。

时间复杂度就变成了:$\mathcal{O(n)}$。

AC CODE:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 200010;
int n;
ll dp[N];
vector <int> e[N] , d[N];
int fa[N] , deep[N] , ma[N] , mi[N] , a[N];
void dfs (int u , int pre) {
	deep[u] = deep[pre] + 1;\/\/深度,他的深度为他的父节点的深度加一
	d[deep[u]].push_back (u);\/\/每一层有那些值
	ma[deep[u]] = max (ma[deep[u]] , a[u]);\/\/每一层的最大值
	mi[deep[u]] = min (mi[deep[u]] , a[u]);\/\/每一层的最小值
	for (auto v : e[u]) {
		if (v == pre) continue;
		fa[v] = u;\/\/预处理fa数组
		dfs (v , u);\/\/递归他的儿子节点
	}
}
void init () {\/\/初始化
	for (int i = 1; i <= n; ++ i) {
		e[i].clear ();
		d[i].clear ();
		dp[i] = fa[i] = deep[i] = ma[i] = 0;
		mi[i] = 2e9;
	}
}
int main() {
	memset (mi , 0x3f , sizeof (mi));
	int T;
	scanf ("%d" , &T);
	while (T --) {
		init ();
		scanf ("%d" , &n);
		for (int i = 2; i <= n; ++ i) {
			int x;
			scanf ("%d" , &x);
			e[x].push_back (i);\/\/连边
			e[i].push_back (x);
		}
		for (int i = 2; i <= n; ++ i) scanf ("%d" , &a[i]);
		dfs (1 , 1);\/\/预处理
		ll ans = 0;
		for (int i = 2; i <= n; ++ i) {
			ll val1 = 0 , val2 = -2e9;\/\/初始化
			for (auto x : d[i]) {\/\/就是预处理f,g
				val1 = max (val1 , dp[fa[x]] + a[x]);
				val2 = max (val2 , dp[fa[x]] - a[x]);
			}
			for (auto x : d[i]) {
				dp[x] = max (dp[x] , dp[fa[x]] + max (ma[i] - a[x] , a[x] - mi[i]));\/\/第一个转移方程 
				dp[x] = max (dp[x] , max (val1 - a[x] , val2 + a[x]));\/\/第二个 
				ans = max (ans , dp[x]);\/\/求个最大值 
			}
		}
		printf ("%lld\n" , ans);\/\/输出
	}
	return 0;
}

码字不易,求赞!

CF1100C题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-05-01 20:08:34

题解思路:

图就是一个内接圆,外面有一圈外接圆。

外接圆有 $n$ 个。

我们就设内接圆的半径为 $r$ 外接圆的半径为 $R$

如下图所示:

然后我们随便挑两个圆来看,首先我们先把圆心连起来,因为这 $n$ 个圆是相等的,所以他是等腰三角形,用两个外接圆的重合的那个点和内接圆的圆心连起来就是和外接圆的圆心连起来的边是垂直的,如下图所示:

而在圆心里的哪个角我们能求出来,因为 $n$ 个这样的角组成了一个圆,所以他的角度为:$\frac{2\pi}{n}$。

如图:

那么这个角的一半就是: $\frac{\pi}{n}$。

那么 $\frac{R}{R + r} = \sin \frac{\pi}{n} \longrightarrow R = \frac{r \sin\frac{\pi}{n}}{1 - \sin \frac{\pi}{n}}$。

公式好了,就可以写代码了!

AC CODE:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const double PI = 3.1415926;
double n , r;
int main() {
    cin >> n >> r;
    printf ("%.7lf\n" , (double)(r * sin (PI \/ n)) \/ (1.0 - sin (PI \/ n)));\/\/直接套用公式即可
    return 0;
}

码字不易,求赞!

CF1100E题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-05-02 14:44:08

题解思路:

样例:

前置知识:

图中无环的条件是什么?

就是一个点无法走一圈回到自己,他就无环。

那么有一个算法:topsort(拓扑排序)

topsort 就会给每个节点一个拓扑值

图例:

若一个图有环,那么他就没有拓扑序,因为他所有的点的入度都 $\ne 0$。

若一个点有一个起点终点 $\langle u , v\rangle$,若 $top_{u} < top_{v}$,那么这个图就是无环的。

或拓扑排序不能让他的入度为 $0$,那么他就是有环的。

然后我们来看一下这个题怎么做:

首先他让我们求最大值最小,那么根据经验应该是二分答案。

然后我们就上那方面上想。

把他得到的权值设成 $mid \longrightarrow$ 图中改变方向的边的权值最大值。

我们设 $w$ 为某个点的权值。

我们就先二分答案:

$w\le mid$ 不加进图,就相当于把他删掉。

$w > mid$ 按原样加进图。

判定:

若我们把 $\le mid$ 的边都删掉,他还是有环,那么就是删的边数太少了,那么把删边数的多一点。

若没有环了,就让答案的权值小一点。

若我们求出来了一个 $mid \longrightarrow$ 保证图中无环

$w \le mid$ 我们就把他看作无向边。

那么我们要反转那些边呢??

拓扑排序出来的数组 $top$,若 $top_{u} > top_{v}$ 那么我们就反转这条边。

AC CODE

码字不易,求赞!

共 105 篇博客