CodePlus 第五次网络赛 掐指会算

失踪人口暂时回归。
临近 NOIP 了,退役选手准备打一打 Div.2 来练练手(应该不是天气冷了,没衣服穿了
游戏体验差,OJ 又和第一次一样卡了半天。


T1 我有矩阵,你有吗?

根据异或的性质,不难发现 A A A 矩阵的每行每列最多只能异或一次。
所以我们可以假设 A A A 矩阵的第一行是否被异或了,然后把所有状态递推出来,最后判断一下是否符合题设。

#include <bits/stdc++.h>
using namespace std;
int F()
{
	int x = 0; char ch; bool minus = 0;
	while(ch = getchar(), (ch > '9' || ch < '0') && ch != '-');
	ch == '-' ? minus = 1 : x = ch - '0';
	while(ch = getchar(), ch <= '9' && ch >= '0') x = x * 10 + ch - '0';
	return minus ? -x : x;
}

int n, m, x[1010], y[1010], A[1010][1010], B[1010][1010];
int main()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			scanf("%d", &A[i][j]);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			scanf("%d", &B[i][j]);
	for(int i = 1; i <= m; ++i) if(A[1][i] != B[1][i]) y[i] = 1;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if((A[i][j] ^ y[j] ^ x[i]) != B[i][j]) x[i] ^= 1;
	bool flag = 0;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if((A[i][j] ^ x[i] ^ y[j]) != B[i][j])
				flag = 1;
	if(!flag){ puts("Koyi"); return 0; }
	memset(x, 0, sizeof(x)); memset(y, 0, sizeof(y));
	x[1] = 1;
	for(int i = 1; i <= m; ++i) if(A[1][i] == B[1][i]) y[i] = 1;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if((A[i][j] ^ y[j] ^ x[i]) != B[i][j]) x[i] ^= 1;
	flag = 1;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if((A[i][j] ^ x[i] ^ y[j]) != B[i][j])
				flag = 1;
	if(!flag){ puts("Koyi"); return 0; }
	puts("Budexing");
	return 0;
}

T2 逻辑树

一开始没看到在询问的都是叶结点,想了半天。

我想到的是一种构造方式,这种构造方式可以保证所有询问序列都是必要的,且根结点的取值一定为 1 1 1

假设只有三个点(一个根和两个叶子, a a a 点为先询问的叶子, b b b 为后询问的),假设根的运算符为 and ,那么 a a a 显然要取值为 1 1 1,才能保证询问 b b b 时不会跳过,若为 or,则 a a a 要为 0 0 0,按照这样的取值,根结点的取值就依赖于 b b b 了。

如果有很多点,我们可以按照询问的叶子的顺序,每次找到这个叶子的祖先第一个取值还未被确定的点(即这个点的取值不依赖与任意一个叶子),然后如果是 and,则该叶子取值为 1 1 1,为 or 则为 0 0 0

需要注意的是如果是最后一个叶子,那么对应的祖先一定是根结点,而此时根结点的取值依赖与最后一个叶子的取值,所以最后一个叶子取值为 1 1 1

这样做的复杂度为 O ( n ) O(n) O(n),应为每一个点都只会被找两次。

#include <bits/stdc++.h>
using namespace std;
#define Min(_A, _B) (_A < _B ? _A : _B)
#define Max(_A, _B) (_A > _B ? _A : _B)
int F()
{
	int x = 0; char ch; bool minus = 0;
	while(ch = getchar(), (ch > '9' || ch < '0') && ch != '-');
	ch == '-' ? minus = 1 : x = ch - '0';
	while(ch = getchar(), ch <= '9' && ch >= '0') x = x * 10 + ch - '0';
	return minus ? -x : x;
}

const int MaxN = 500010;
int Next[MaxN << 2], Point[MaxN << 1], To[MaxN << 2], q, P[MaxN << 1];
void Add(int u, int v)
{
	Next[++q] = Point[u]; Point[u] = q; To[q] = v;
	Next[++q] = Point[v]; Point[v] = q; To[q] = u;
}
int N, f[MaxN << 1][2], p[MaxN << 1][2], w[MaxN << 1], l[MaxN << 1], r[MaxN << 1], fa[MaxN << 1];
void dfs(int u, int from)
{
	fa[u] = from;
	for(int j = Point[u]; j; j = Next[j]) if(To[j] != from)
	{
		if(!l[u]) l[u] = To[j]; else r[u] = To[j];
		dfs(To[j], u);
	}
}
int ans[MaxN], pre[MaxN], Q[MaxN << 1], cnt;
bool vis[MaxN];
int find(int u){ return u == pre[u] ? u : pre[u] = find(pre[u]); }
int main()
{
	scanf("%d", &N);
	for(int i = 1; i < N; ++i) scanf("%d", &w[i + N]);
	for(int i = 1; i <= 2 * N - 2; ++i) Add(F(), F());
	for(int i = 1; i <= N; ++i) P[i] = F();
	dfs(N + 1, 0);
	for(int i = 1; i <= N; ++i) pre[i] = i;
	for(int i = 1; i <= N; ++i)
	{
		vis[P[i]] = 1;
		int t = P[i];
		while(fa[t] && vis[l[fa[t]]] && vis[r[fa[t]]]) 
		{
			t = fa[t];
			vis[t] = 1;
		}
		if(fa[t]) ans[P[i]] = !w[fa[t]];
		else ans[P[i]] = 1;
	}
	
	for(int i = 1; i <= N; ++i) printf("%d", ans[i]);
	return 0;
}

T3 监控中心

认真分析题目发现,题目其实是给定一张图,图中的每个点要么属于 A A A,要么属于 B B B,告诉你哪些边连接着不同集合中的元素,求 ∣ A ∣ |A| A

首先看子任务 1 1 1,一条链。我们发现如果有连续的几个 A A A 中的元素被夹在 B B B 中,那么我们可以利用后缀和的方式,将两个边界处的元素的位置相减,得到被夹在中间的 A A A 中元素的个数。

再来看子任务 2 , 3 2,3 2,3,一棵树。道理和子任务 1 1 1 是类似的,我们只需要将后缀和改为子树和,然后也可以利用加减来得出元素的个数。需要注意你求的集合是否为 A A A

子任务 4 4 4,一张图。找出任意的生成树,按照树来做。因为图给出的边有很多是没有价值的,多余的。

#include <bits/stdc++.h>
using namespace std;
int F()
{
	int x = 0; char ch; bool minus = 0;
	while(ch = getchar(), (ch > '9' || ch < '0') && ch != '-');
	ch == '-' ? minus = 1 : x = ch - '0';
	while(ch = getchar(), ch <= '9' && ch >= '0') x = x * 10 + ch - '0';
	return minus ? -x : x;
}
#define Abs(_A) (_A > 0 ? _A : -(_A))
const int MaxN = 1000010, MaxM = 2500010;
int n, m, Q[10000010];
struct Edge { int u, v; } e[MaxM];
int Next[MaxN << 1], To[MaxN << 1], Point[MaxN], q;
bool vis[MaxM];
void Add(int u, int v)
{
	Next[++q] = Point[u]; Point[u] = q; To[q] = v;
	Next[++q] = Point[v]; Point[v] = q; To[q] = u;
}
int l[MaxN], sum[MaxN], dfn_index;
void dfs(int u, int from)
{
	l[u] = ++dfn_index; ++sum[u];
	for(int j = Point[u]; j; j = Next[j]) if(To[j] != from)
	{
		dfs(To[j], u);
		sum[u] += sum[To[j]];
	}
}
int pre[MaxN];
int find(int u){ return u == pre[u] ? u : pre[u] = find(pre[u]); }
int main()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; ++i) e[i] = (Edge){F(), F()};
	for(int i = 1; i <= n; ++i) pre[i] = i;
	for(Edge *i = e + 1; i <= e + m; ++i)
	{
		int X = find(i->u), Y = find(i->v);
		if(X != Y)
		{
			pre[X] = Y;
			Add(i->u, i->v);
			vis[i - e] = 1;
		}
	}
	dfs(1, 0);
	int q;
	scanf("%d", &q);
	for(int i = 1; i <= q; ++i)
	{
		int c, tot = 0, pos = 0, tmp = 2147483647; scanf("%d", &c);
		for(int j = 1; j <= c; ++j) 
		{
			scanf("%d", &Q[j]);
			if(!vis[Abs(Q[j])]) continue;
			if(l[e[Abs(Q[j])].u] < tmp) tmp = l[e[Abs(Q[j])].u], pos = e[Abs(Q[j])].u;
			if(l[e[Abs(Q[j])].v] < tmp) tmp = l[e[Abs(Q[j])].v], pos = e[Abs(Q[j])].v;
		}
		bool flag = 1;
		for(int j = 1; j <= c; ++j)
		{
			int u, v;
			if(!vis[Abs(Q[j])]) continue;
			if(Q[j] > 0) u = e[Q[j]].u, v = e[Q[j]].v;
			if(Q[j] < 0) u = e[-Q[j]].v, v = e[-Q[j]].u;
			if(pos == v) flag = 0;
			if(l[u] < l[v]) tot -= sum[v];
			else tot += sum[u];
		}
		if(!flag) tot = n - tot;
		printf("%d\n", Abs(tot));
	}
	return 0;
}

T4 法师

目测一道计算几何题,Div.2 全场无人 AC,而且还没部分分(有我应该也写不出来)


rank 20- 应该是有件衣服过冬了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值