[題解]CSP2019 Solution - Part A

  • 至於爲什麼是 Part A\text{Part A} 而不是 Day 1\text{Day 1}
  • 那是因爲 Day1 T3 還沒改
  • (那這六題的 solution\text{solution} 就按難度順序寫吧)
  • 感覺今年的畫風和 NOIP 2016\text{NOIP 2016} 有點像?

D1T1 code

Solution

  • 直接模擬
  • 如果 k<2n1k<2^{n-1} 就輸出 00
  • 否則輸出 11 並把 kk 變成 2n1k2^n-1-k
  • 然後 nn 減掉 11 繼續進行下去直到 n=0n=0 爲止
  • 注意 kk 要開 unsigned long long\text{unsigned long long}
  • O(n)O(n)

Code

#include <bits/stdc++.h>

typedef unsigned long long ull;

int n;
ull k;

void solve(int n, ull k)
{
	if (!n) return;
	ull mid = 1ull << n - 1;
	if (k < mid) putchar('0'), solve(n - 1, k);
	else putchar('1'), solve(n - 1, mid - 1 - (k - mid));
}

int main()
{
	std::cin >> n >> k;
	solve(n, k);
	return puts(""), 0;
}

D1T2 brackets

Solution

  • cntucnt_u 表示根到 uu 的路徑組成的括號序列,以 uu 爲右端點的合法括號序列個數
  • 那麼 kuk_u 就等於根到 uu 的路徑上所有點的 cntcnt 之和
  • 易得如果存在 uu 的一個深度最大的祖先 vv 使得 vvuu 的路徑組成的括號序列是合法括號序列
  • 那麼 cntu=cntfav+1cnt_u=cnt_{fa_v}+1
  • 對於求這個 vv ,可以維護一個棧
  • 從根到 uu ,如果是左括號則直接加入,如果是右括號且棧不空則彈棧
  • 那麼如果 uu 爲右括號,那麼 vv 爲這次彈出的括號對應的點
  • 而對於求出所有的 uu ,可以在對樹 DFS\text{DFS} 的過程中維護這個棧,在 DFS\text{DFS} 回溯時把棧操作也退回即可
  • O(n)O(n)

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

typedef long long ll;

const int N = 5e5 + 5;

int n, fa[N], ecnt, nxt[N], adj[N], go[N], stk[N], top, cnt[N];
char s[N];
ll sum[N], ans;

void add_edge(int u, int v)
{
	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}

void dfs(int u)
{
	int tf = 0;
	if (s[u] == '(') stk[++top] = u;
	else if (top) cnt[u] = cnt[fa[tf = stk[top--]]] + 1;
	sum[u] = sum[fa[u]] + cnt[u];
	ans ^= sum[u] * u;
	for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
		dfs(v);
	if (s[u] == '(') top--;
	else if (tf) stk[++top] = tf;
}

int main()
{
	int x;
	read(n);
	scanf("%s", s + 1); n = strlen(s + 1);
	for (int i = 2; i <= n; i++) read(x), add_edge(fa[i] = x, i);
	dfs(1);
	return std::cout << ans << std::endl, 0;
}

D2T1 meal

Solution

  • 如果沒有一半的限制,那麼答案爲
  • i=1n(1+j=1mai,j)1\prod_{i=1}^n(1+\sum_{j=1}^ma_{i,j})-1
  • 而出現次數超過一半的主要食材最多 11
  • 故可以枚舉超過一半的主要食材是哪種,並把對應的方案數從上式種扣掉即可
  • 假設確定了一種食材 xx ,考慮如何求這種食材出現超過一半的方案數
  • ui=ai,xu_i=a_{i,x}vi=j=1,jxmai,jv_i=\sum_{j=1,j\ne x}^ma_{i,j}
  • 問題就轉化成了有 nn 個變量,對於第 ii 個變量有 uiu_i 種方法使其爲 11viv_i 種方法使其爲 1-111 種方法使其爲 00,求有多少種方案使得 11 的個數嚴格大於 1-1 (所有變量的和嚴格大於 00
  • 於是可以 DP\text{DP} :設 f[i][j]f[i][j] 表示前 ii 個變量和爲 jj 的方案數(jj 可以爲負),轉移時枚舉下一個變量的取值
  • O(mn2)O(mn^2)

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

const int N = 105, E = 205, M = 2005, rqy = 998244353;

int n, m, a[N][M], f[N][E], sum[N], tmp[N], ans = 1;

inline void add(int &a, const int &b)
{
	a += b; if (a >= rqy) a -= rqy;
}

inline void sub(int &a, const int &b)
{
	a -= b; if (a < 0) a += rqy;
}

int main()
{
	read(n); read(m);
	for (int i = 1; i <= n; i++) sum[i] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			read(a[i][j]), add(sum[i], a[i][j]);
	for (int i = 1; i <= n; i++) ans = 1ll * ans * sum[i] % rqy;
	sub(ans, 1);
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= n; j++) tmp[j] = sum[j], sub(tmp[j], a[j][i]), sub(tmp[j], 1);
		for (int j = -n; j <= n; j++)
			for (int k = 0; k <= n; k++)
				f[k][j + n] = 0;
		f[0][n] = 1;
		for (int j = 1; j <= n; j++)
			for (int k = -n; k <= n; k++)
			{
				add(f[j][k + n], f[j - 1][k + n]);
				if (k > -n) add(f[j][k + n], 1ll * f[j - 1][k - 1 + n] * a[j][i] % rqy);
				if (k < n) add(f[j][k + n], 1ll * f[j - 1][k + 1 + n] * tmp[j] % rqy);
			}
		for (int j = 1; j <= n; j++) sub(ans, f[n][j + n]);
	}
	return std::cout << ans << std::endl, 0;
}
發佈了410 篇原創文章 · 獲贊 53 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章