Educational Codeforces Round 107 (Rated for Div. 2)

Educational Codeforces Round 107 (Rated for Div. 2)

A. Review Site

要让总的向上的票数最多,由于第一种和第二种票是固定的,只要让第三种票尽量向上就行了,由于有两个投票机,可以让其中一个给第一种和第二种的用,还有一个给第三种的,这样保证了每次第三种投票投的都是向上的票,所以答案就是第一种票数加上第三种票数

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

int T, n, ans;

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

inline void write(int x)
{
	if (x < 0) putchar('-'), x = -x;
	int num = 0;
	char sc[15];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar('\n');
}

int main()
{
	T = read();
	while (T--)
	{
		n = read(), ans = 0;
		for (Re i = 1; i <= n; ++i)
		{
			int u = read();
			if (u == 1 || u == 3) ++ans;
		}
		write(ans);
	}
	return 0;
}

B. GCD Length

\(a,b\geq c\),可以另\(gcd(x,y)=10^{c-1}\),让\(x,y\)的后\(c-1\)位为\(0\),这样就是要构造\(gcd(\frac{x}{10^{c-1}},\frac{y}{10^{c-1}})=1\)\(x,y\),另\(a \leq b\),当\(a < b\)\((a = b\)\(a > c)\)时,只需要让\(x\)的第\(a,c\)位为\(1\),让\(y\)\(b\)位为\(1\),其他位均为\(0\),否则,当\(a = b = c\)时,只需要让\(x=y\)即可

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

int T, a, b, c;

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

int main()
{
	T = read();
	while (T--)
	{
		a = read(), b = read(), c = read();
		if (a < b || (a == b && a > c))
		{
			putchar(49);
			for (Re i = 1; i < a; ++i) putchar(48);
			putchar(' ');
			putchar(49);
			for (Re i = 1; i < b - c; ++i) putchar(48);
			putchar(49);
			for (Re i = 1; i < c; ++i) putchar(48);
		}
		else if (a > b)
		{
			putchar(49);
			for (Re i = 1; i < a - c; ++i) putchar(48);
			putchar(49);
			for (Re i = 1; i < c; ++i) putchar(48);
			putchar(' ');
			putchar(49);
			for (Re i = 1; i < b; ++i) putchar(48);
		}
		else
		{
			putchar(49);
			for (Re i = 1; i < c; ++i) putchar(48);
			putchar(' ');
			putchar(49);
			for (Re i = 1; i < c; ++i) putchar(48);
		}
		putchar('\n');
	}
	return 0;
}

C. Yet Another Card Deck

对于一个数\(x\),不管它出现了几次,答案只和它出现的第一次有关,将\(x\)移到第一位,那么原本在\(x\)前面的数的位置\(+1\),暴力做就行

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

int n, q, t[55];

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

inline void write(int x)
{
	if (x < 0) putchar('-'), x = -x;
	int num = 0;
	char sc[15];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar(' ');
}

int main()
{
	n = read(), q = read();
	for (Re i = 1; i <= n; ++i)
	{
		int u = read();
		if (!t[u]) t[u] = i;
	}
	while (q--)
	{
		int u = read();
		write(t[u]);
		for (Re i = 1; i <= 50; ++i)
			if (t[i] && t[i] < t[u]) ++t[i];
		t[u] = 1;
	}
	return 0;
}

D. Min Cost String

它一共的对数一共有\(k^{2}\)个,手玩可以发现,可以如下构造\((k=4)\):a-a、a-d-a、a-c-a、a-b、b-b、b-d-b、b-c、c-c、c-d、d-d、d-c-b-a,类似这样的构造方法,可以把\(k^{2}\)个不重地构造出来,然后循环即可

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

int n, k, s, a[700];

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

int main()
{
	n = read(), k = read(), s = 1;
	for (Re i = 0; i < k; ++i)
		for (Re j = k - 1; j > i; --j) a[++s] = i, a[++s] = j;
	for (Re i = k - 1; i; --i) a[++s] = i;
	for (Re i = 0; i < n; ++i) putchar(97 + a[i % s + 1]);
	return 0;
}

E. Colorings and Dominoes

容易发现,答案就是统计对于任意一个多米诺骨牌的放置位置,放置了这个后,其他方格被染色的所有方案数之和,为了避免重复,强制让放置多米诺骨牌能靠左/上就尽量靠左/上
对于你当前在某个位置上放了一个横着的多米诺骨牌,在你左边的连续红色的格子数应为偶数个,记你当前多米诺骨牌的左边连续格子数为\(x\),总的格子数为\(y\),答案就是\(\sum_{i = 1, i += 2}^{i <= x} 2^{y - i - 2} + ((2 | x) ? 2^{y - x - 2} : 0)\),要注意边界情况,要计算\(2\)的幂的每次间隔\(2\)的前缀和,提前预处理即可
至于竖着的的多米诺骨牌和横着的类似

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 300005;
const int p = 998244353;
int n, m, t, ans, f[N], g[N];
vector<bool> b[N];
vector<int> h[N], w[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

inline int inc(int x, int y)
{
	x += y;
	return (x < p) ? x : x - p;
}

int main()
{
	n = read(), m = read();
	for (Re i = 0; i <= m; ++i) b[0].push_back(0), h[0].push_back(0), w[0].push_back(0);
	for (Re i = 1; i <= n; ++i)
	{
		b[i].push_back(0), h[i].push_back(0), w[i].push_back(0);
		for (Re j = 1; j <= m; ++j)
		{
			char c = getchar();
			while (c ^ '*' && c ^ 'o') c = getchar();
			if (c ^ '*') ++t, b[i].push_back(1), h[i].push_back(h[i - 1][j] + 1), w[i].push_back(w[i][j - 1] + 1);
			else b[i].push_back(0), h[i].push_back(0), w[i].push_back(0);
		}
	}
	f[0] = g[0] = 1, f[1] = g[1] = 2;
	for (Re i = 2; i <= t; ++i)	f[i] = inc(f[i - 1], f[i - 1]), g[i] = inc(g[i - 2], f[i]);
	for (Re i = 1; i <= n; ++i)
		for (Re j = 1; j <= m; ++j)
			if (b[i][j])
			{
				if (h[i][j] >= 2)
				{
					int u = h[i][j] + 1;
					if (!(u & 1)) ++u;
					if (t >= 3) ans = inc(ans, g[t - 3]);
					if (t >= u) ans = inc(ans, p - g[t - u]);
					if (!(h[i][j] & 1)) ans = inc(ans, f[t - h[i][j]]);
				}
				if (w[i][j] >= 2)
				{
					int u = w[i][j] + 1;
					if (!(u & 1)) ++u;
					if (t >= 3) ans = inc(ans, g[t - 3]);
					if (t >= u) ans = inc(ans, p - g[t - u]);
					if (!(w[i][j] & 1)) ans = inc(ans, f[t - w[i][j]]);
				}
			}
	printf("%d", ans);
	return 0;
}

F. Chainword

可以将所有字符串建一棵\(trie\)树,裸的\(dp\)方程\(f[i][u][v]\)表示当前在第\(i\)位,在\(trie\)树上的点分别是\(u\)\(v\)\(u,v\)最多有\(41\)种取值
由于位数最大为\(10^{9}\),可以矩阵转移优化\(dp\),记\(f[i][x]\)表示当前在第\(i\)位,且对应的状态为\(trie\)树上的某个点对,这样\(x\)最多有\(41\cdot 41 = 1681\)种取值,矩阵大小最多为\(1681 \cdot 1681 = 2825761\),明显是不行的
发现很多状态其实是不存在的,一个状态\(x\)对应的\(trie\)树上两个点\(u,v\),它们在\(trie\)树上一定满足跟到其中一个点所代表的字符串是跟到另一个点所代表的的字符串的后缀,这样状态最多只有\(40 \cdot 6 + 1 = 241\)种取值,但是还是不够优秀,可以把\((u,v)\)\((v,u)\)合并在一起,这样状态最多只有\(8 \cdot (2 + 3 + 4 + 5 + 6) + 1 = 161\)种,暴力枚举出所以状态及状态与状态之间的转移
由于实际上根本跑不满,没有那么多种状态,所以可以较快通过

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 180, p = 998244353;
struct mat
{
	int a[N][N];
}mt, f;
struct info
{
	int l, r;
}a[N];
int n, m, len, num, res, fa[45], ch[45], siz[45], tr[45][28], g[45][45];
char s[10];

inline mat operator * (mat x, mat y)
{
	mat z;
	for (Re i = 0; i < res; ++i)
		for (Re j = 0; j < res; ++j)
		{
			z.a[i][j] = 0;
			for (Re k = 0; k < res; ++k) z.a[i][j] = (1ll * x.a[i][k] * y.a[k][j] + z.a[i][j]) % p;
		}
	return z;
}

inline mat fpow(mat x, int y)
{
	mat z;
	for (Re i = 0; i < res; ++i)
		for (Re j = 0; j < res; ++j) z.a[i][j] = (i == j);
	while (y)
	{
		if (y & 1) z = z * x;
		x = x * x, y >>= 1;
	}
	return z;
}

int main()
{
	scanf("%d %d", &n, &m);
	for (Re i = 0; i < n; ++i)
	{
		scanf("%s", s), len = strlen(s);
		int now = 0;
		for (Re j = 0; j < len; ++j)
		{
			int u = s[j] - 97;
			if (!tr[now][u]) fa[tr[now][u] = ++num] = now, ch[num] = u;
			now = tr[now][u];
		}
		++siz[now];
	}
	for (Re i = 0; i <= num; ++i)
	{
		a[g[0][i] = g[i][0] = ++res].l = 0, a[res].r = i;
		for (Re j = 1; j <= i; ++j)
			if (ch[i] == ch[j] && g[fa[j]][fa[i]]) a[g[i][j] = g[j][i] = ++res].l = j, a[res].r = i;
	}
	for (Re i = 0; i < res; ++i)
	{
		int u = a[i + 1].l, v = a[i + 1].r;
		for (Re j = 0; j < 26; ++j)
			if (tr[u][j] && tr[v][j])
			{
				int uu = tr[u][j], vv = tr[v][j];
				++mt.a[i][g[uu][vv] - 1], mt.a[i][g[0][vv] - 1] += siz[uu], mt.a[i][g[0][uu] - 1] += siz[vv], mt.a[i][0] += siz[uu] * siz[vv];
			}
	}
	f.a[0][0] = 1;
	f = f * fpow(mt, m);
	printf("%d", f.a[0][0]);
	return 0;
}

G. Chips on a Board

很容易发现,这题和行数\(n\)没有关系,只要记录每一列有奇数个数还是偶数个数即可,很明显的\(nim\)游戏,但是暴力会\(T\)飞(似乎开\(O3\)\(Ofast\)之类的可以水过去?)
发现在线很难处理,考虑离线
将询问\((l,r)\)拆开,拆成\(Q(l,l)\)\(Q(l,r+1)\),其中\(Q(x,y)\)表示对于所有列数\(≥y\)的值\(z\)\(z-x\)的异或和
\(x\)变化时,\(z-x\)的低位变化频率高,而高位变化频率低,考虑类似根号分治的做法,取一个值\(2^{k}\)
对于低位数,记录所有\(≥y\)的数对\(2^{k}\)取模后的值即可计算,复杂度是\(\Theta(q \cdot 2^{k})\)
对于高位数,由于它差不多每\(2^{k}\)变化一次,暴力把它的所有情况记录在树状数组即可,复杂度是\(\Theta(m \cdot \frac{m}{2^{k}} \cdot log\ m)\)
合在一起复杂度就是\(\Theta(n \cdot \sqrt{n \cdot log\ n})\)(\(n、m、q\)是一个量级的,这里就全采用\(n\)了)
这里我取的是\(k=10\)

#include<bits/stdc++.h>
using namespace std;
#define Re register int

const int N = 200005, K = 10, S = 1024;
struct info
{
	int l, d;
};
int n, m, f[N], ans[N];
bool cnt[N], res[S + 5];
vector<info> q[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline void add(int x, int y)
{
	while (x <= m) f[x] ^= y, x += x & -x;
}

inline int que(int x)
{
	int ans = 0;
	while (x) ans ^= f[x], x -= x & -x;
	return ans;
}

int main()
{
	n = read(), m = read();
	for (Re i = 0; i < n; ++i) cnt[read()] ^= 1;
	n = read();
	for (Re i = 0; i < n; ++i)
	{
		int u = read(), v = read();
		q[u].push_back(info{u, i}), q[v + 1].push_back(info{u, i});
	}
	for (Re i = m; i; --i)
	{
		if (cnt[i])
		{
			res[i % S] ^= 1;
			for (Re j = i, k = 0; j > 0; j -= S, ++k) add(max(1, j - S + 1), k), add(j + 1, k);
		}
		int u = q[i].size();
		for (Re j = 0; j < u; ++j)
		{
			int v = q[i][j].l, s = que(v) << K;
			for (Re k = 0; k < S; ++k)
			{
				if (!res[k]) continue;
				int w = (k - v) % S;
				if (w < 0) w += S;
				s ^= w;
			}
			ans[q[i][j].d] ^= s;
		}
	}
	for (Re i = 0; i < n; ++i) putchar(65 + (!ans[i]));
	return 0;
}
posted @ 2021-04-18 15:38  clfzs  阅读(114)  评论(0)    收藏  举报