NOI Online Round 2 提高组 线上自闭记

。_饼干妹妹 提交于 2020-04-26 13:59:38

今天没吃早餐好难受啊啊啊。

考试之前在打昨天牛客网的一道ds题,要调自闭了。

到考试了还没有写完,心态逐渐暴躁。


\(T_1 : \texttt{color}\)

先把 \(k = 1\)\(x = y\) 的点给特判掉。

然后令 \(x < y\),显然要贪心地去选 \(y\) ,因为每两个 \(y\) 中间一定会有一个 \(x\),一定满足限制条件。

\(y\) 的作用其实可以看做是分割 \(x\),然后问题变成了求一段最多有多少个 \(x\)

显然 \(x\) 开头位置越前面越优秀(在一段里面),根据裴蜀定理可以知道最前面的位置其实是 \(\gcd(x,y)\)

那么最多一段出现次数其实就是 \(1 + \lfloor \frac {y - gcd(x,y)} {x} \rfloor\)

心路历程:

先想到了贪心,然后直接想到了裴蜀定理。

大概 10 min 就做完了,也没怎么去管这道题了。

期望得分:100 pts

代码

#include <bits/stdc++.h>

using namespace std;

inline int read() {
	int x = 0; char ch = getchar();
	while(!isdigit(ch)) ch = getchar();
	while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
	return x;
}

inline int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }

int main() {
	freopen("color.in", "r", stdin);
	freopen("color.out", "w", stdout);
	int Case = read(), x, y, g, r, k;
	while(Case--) {
		x = read(), y = read(), k = read();
		if(k == 1) {
			puts("No");
			continue;
		} else if(x == y) {
			puts("Yes");
			continue;
		}
		if(x > y) swap(x, y);
		g = gcd(y, x);
		r = 1 + (y - g - 1) / x;
		puts(r < k ? "Yes" : "No");
	}
	return 0;
}

\(T_2 : \texttt{sequence}\)

考虑答案没有那个平方怎么做。

从左往右边扫,统计以 \(i\) 为右端点的 \(f\) 之和。

记录 \(las_x\) 表示上一个 \(x\) 出现的位置。

然后加入一个数 \(a_i\) 就相当于把 \(idx \in (las_{a_i}, i]\)\(f\) 值全部加上 \(1\)

用区间修改区间查询的线段树就能维护。

现在考虑答案带上平方。

\((x + d)^2 = x^2 + 2dx + d^2\),线段树维护平方和,区间和,区间长度就能做了。

注意:线段树里面不要取模,开 unsigned long long 就行。不然好像跑不过

心路历程:

一开始看到这道题,感觉如果没有平方的话就直接从左往右扫一遍然后用些简单的数据结构就能做了。

后面想了想,发现这道题应该也是比较简单,应该把维护和变成维护平方和就能做了。

然后大概写了 40 min,后面还写了暴力拍了一下。

期望得分:100 pts

代码

#include <bits/stdc++.h>
#define N 1000005
#define Siz (N << 2)

using namespace std;

typedef unsigned long long ULL;

const int mod = 1e9 + 7;

int n, ll, rr;
int a[N], las[N];
int disc[N];

inline int read() {
	int x = 0; char ch = getchar();
	while(!isdigit(ch)) ch = getchar();
	while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
	return x;
}

namespace SMT {
#define lch(x) (x << 1)
#define rch(x) (x << 1 | 1)
	ULL s2[Siz], s1[Siz];
	int s0[Siz], tag[Siz];
	inline void update(int u) {
		s2[u] = s2[lch(u)] + s2[rch(u)];
		s1[u] = s1[lch(u)] + s1[rch(u)];
	}
	inline void pushdown(int u) {
		if(tag[u]) {
			int l = lch(u), r = rch(u), d = tag[u];
			s2[l] = s2[l] + 2 * d * s1[l] + 1ULL * s0[l] * d * d;
			s1[l] = s1[l] + 1ULL * s0[l] * d;
			tag[l] = tag[l] + d;
			s2[r] = s2[r] + 2 * d * s1[r] + 1ULL * s0[r] * d * d;
			s1[r] = s1[r] + 1ULL * s0[r] * d;
			tag[r] = tag[r] + d;
			tag[u] = 0;
		}
	}
	void build(int u, int l, int r) {
		s0[u] = r - l + 1;
		if(l == r) return void();
		int mid = l + r >> 1;
		build(lch(u), l, mid);
		build(rch(u), mid + 1, r);
	}
	void modify(int u, int l, int r) {
		if(ll <= l && r <= rr) {
			s2[u] = s2[u] + 2 * s1[u] + s0[u];
			s1[u] = s1[u] + s0[u];
			++tag[u];
			return void();
		} pushdown(u);
		int mid = l + r >> 1;
		if(ll <= mid) modify(lch(u), l, mid);
		if(mid < rr) modify(rch(u), mid + 1, r);
		update(u);
	}
	ULL query(int u, int l, int r) {
		if(ll <= l && r <= rr) return s2[u];
		pushdown(u);
		int mid = l + r >> 1;
		if(rr <= mid) return query(lch(u), l, mid);
		else if(mid < ll) return query(rch(u), mid + 1, r);
		else return query(lch(u), l, mid) + query(rch(u), mid + 1, r);
	}
#undef lch
#undef rch
}

int main() {
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
	n = read();
	for(int i = 1; i <= n; ++i)
		disc[i] = a[i] = read();
	sort(disc + 1, disc + n + 1);
	int tot = unique(disc + 1, disc + n + 1) - disc - 1;
	for(int i = 1; i <= n; ++i)
		a[i] = lower_bound(disc + 1, disc + tot + 1, a[i]) - disc;
	
	int ans = 0;
	SMT::build(1, 1, n);
	for(int i = 1; i <= n; ++i) {
		ll = las[a[i]] + 1, rr = i;
		SMT::modify(1, 1, n);
		las[a[i]] = i;
		ll = 1, rr = i;
		ans = (ans + SMT::query(1, 1, n)) % mod;
	}
	printf("%d\n", ans);
	return 0;
}

我现在感觉稳得一批,去到学校的小卖部去买东西吃去了。

浪浪浪,颓颓颓。


\(T_3 : \texttt{match}\)

出题人出来挨打!

这题树上背包要跑 \(O(n^2)\) 显然是要利用\(\color{DeepSkyBlue} {这道题}\)的 trick。

第一遍题意感觉很简单,直接上背包就行。

但是我快打完的时候。。。题目更新了,与原题意完全不同。wdnmd

第二遍题意感觉麻烦一些,可能要容斥之类的。

记录 \(f_{u,x}\) 表示 \(u\) 的子树内匹配了至少 \(x\) 对的方案数,最后应该乘上个系数就行。

然后我也很艰难地码了出来,但是一直都没调对,开始自闭。

后面发现题目看错了,游戏的顺序是无序的,转移时候不需要乘组合数。

最后调出来了大概是 11:59(极限交题),但是。。。NOI 的辣鸡页面上不去了。

期望得分:一分也没有。

代码

#include <bits/stdc++.h>
#define N 5005

using namespace std;

const int mod = 998244353;

int n, m;
int head[N], nex[N << 1], to[N << 1], ecnt;
int fac[N + 5], ifac[N + 5];

inline void addE(int u, int v) {
	to[++ecnt] = v;
	nex[ecnt] = head[u], head[u] = ecnt;
}

int fpm(int x, int y) {
	int r = 1;
	while(y) {
		if(y & 1) r = 1LL * r * x % mod;
		x = 1LL * x * x % mod, y >>= 1;
	}
	return r;
}

inline int perm(int x, int y) { return 1LL * fac[x] * ifac[x - y] % mod; }
inline int comb(int x, int y) { return 1LL * perm(x, y) * ifac[y] % mod; }

int f[N][N], g[N], F[N];
int siz[N], sa[N], sb[N], color[N];

void dfs(int u, int fa) {
	f[u][0] = 1;
	for(int I = head[u], v; I; I = nex[I]) {
		v = to[I]; if(v == fa) continue;
		dfs(v, u);
		for(int i = 0; (i << 1) <= siz[u] + siz[v]; ++i) g[i] = 0;
		for(int i = 0; (i << 1) <= siz[u]; ++i)
			for(int j = 0; (j << 1) <= siz[v]; ++j)
				g[i + j] = (g[i + j] + 1LL * f[u][i] * f[v][j]) % mod;
		siz[u] += siz[v], sa[u] += sa[v], sb[u] += sb[v];
		for(int i = 0; (i << 1) <= siz[u]; ++i) f[u][i] = g[i];
	} ++siz[u];

	if(!color[u]) {
		for(int i = sa[u] - 1; i >= 0; --i)
			f[u][i + 1] = (f[u][i + 1] + 1LL * (sa[u] - i) * f[u][i]) % mod; 
	} else {
		for(int i = sb[u] - 1; i >= 0; --i)
			f[u][i + 1] = (f[u][i + 1] + 1LL * (sb[u] - i) * f[u][i]) % mod;
	}
}

int main() {
	freopen("match.in", "r", stdin);
	freopen("match.out", "w", stdout);
	scanf("%d", &n), m = n >> 1;
	for(int i = 1; i <= n; ++i) {
		char ch = getchar();
		while(!isdigit(ch)) ch = getchar();
		color[i] = ch - '0';
		if(color[i]) ++sa[i];
		else ++sb[i];
	}
	for(int i = 1, u, v; i < n; ++i) {
		scanf("%d %d", &u, &v);
		addE(u, v), addE(v, u);
	}
	
	fac[0] = 1;
	for(int i = 1; i <= N; ++i) fac[i] = 1LL * i * fac[i - 1] % mod;
	ifac[N] = fpm(fac[N], mod - 2);
	for(int i = N; i; --i) ifac[i - 1] = 1LL * i * ifac[i] % mod;
	
	dfs(1, 0);
	
	for(int i = 0; i <= m; ++i)
		F[i] = 1LL * f[1][i] * fac[m - i] % mod;
	for(int i = m; i >= 0; --i)
		for(int j = i - 1; j >= 0; --j)
			F[j] = (F[j] - 1LL * comb(i, j) * F[i] % mod + mod) % mod;
	for(int i = 0; i <= m; ++i)
		printf("%d\n", F[i]);
	return 0;
}

结论:

  1. 写了两题不要感觉自己很稳,要放平心态。
  2. 一定要提前吃早餐。
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!