其他分享
首页 > 其他分享> > AtCoder Beginner Contest 217

AtCoder Beginner Contest 217

作者:互联网

AtCoder Beginner Contest 217

掉大分 QwQ,怎么场场降智啊(

不会 H,埋坑新科技:slope trick

\(A\sim D\)

A,B,C 简单模拟即可,D 直接 set 模拟维护前驱后继(不要和我一样降智写个二分 + 并查集维护)

\(E\)

维护序列,支持三种操作:

  1. 在末尾加入一个数。
  2. 打印序列开头,并删除开头。
  3. 将整个序列从小到大排序。

利用一个指针维护目前有序的最后一个节点,若询问 \(\leq\) 它就直接查询区间 min,否则输出该点。

利用线段树维护即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 2e5 + 10;
const int INF = 2e9;
int n, a[N], dat[N << 2];

int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

void Push(int p) {
	dat[p] = a[dat[p << 1]] < a[dat[p << 1 | 1]] ? 
			dat[p << 1] : dat[p << 1 | 1];
}

void Build(int p, int l, int r) {
	if(l == r) {dat[p] = n + 1; return;}
	int mid = (l + r) >> 1;
	Build(p << 1, l, mid);
	Build(p << 1 | 1, mid + 1, r); Push(p);
}

void Modify(int p, int l, int r, int k, int v) {
	if(l == r) {dat[p] = v; return;}
	int mid = (l + r) >> 1;
	if(k <= mid) Modify(p << 1, l, mid, k, v);
	else Modify(p << 1 | 1, mid + 1, r, k, v); Push(p);
}

int Query(int p, int l, int r, int L, int R) {
	if(L <= l && r <= R) return dat[p];
	int mid = (l + r) >> 1, ans = n + 1;
	if(L <= mid) {
		int now = Query(p << 1, l, mid, L, R) ;
		ans = a[ans] < a[now] ? ans : now;
	}
	if(R >  mid) {
		int now = Query(p << 1 | 1, mid + 1, r, L, R);
		ans = a[ans] < a[now] ? ans : now;
	}
	return ans;
}

int Get(int p, int l, int r) {
	if(l == r) return l;
	int mid = (l + r) >> 1;
	if(dat[p << 1] != n + 1)
		return Get(p << 1, l, mid);
	else
		return Get(p << 1 | 1, mid + 1, r);
}

int main() {
	n = read(); a[n + 1] = INF;
	Build(1, 1, n);
	int L = -1, R = 0, t = 0;
	for(int i = 1; i <= n; i ++) {
		int opt = read();
		if(opt == 1) {
			int x = read(); t ++, a[t] = x;
			if(L == -1) L = t;
			Modify(1, 1, n, t, t);
		}
		else if(opt == 2) {
			int ans = Query(1, 1, n, L, max(L, R));
			printf("%d\n", a[ans]);
			Modify(1, 1, n, ans, n + 1); L = (dat[1] == n + 1) ? -1 : Get(1, 1, n);
		}
		else {
			R = t;
		}
	}
	return 0;
}

\(F\)

给定 \(2n\) 个数,以及 \(m\) 对友好关系,当且仅当两个有友好关系的数字相邻时,可以将它们删除。

问删除整个序列的方案数,若无解输出 -1

读错题直接裂开 QwQ

比较显然的区间 DP 叭,设 \(f(l,r)\) 表示区间的答案,那么有:

\[f(l,r)=\sum\limits_{k=l}^r \text{valid}(l,k)\times f(l+1,k-1)\times f(k+1,r)\times \binom{\frac{r-l+1}{2}}{\frac{k-l+1}{2}} \]

其中 \(\text{valid(l,k)}\) 为 \(1\),仅当 \(l,k\) 有关系,且 \([l,r],[l,k]\) 的区间长度都为偶数。

相当于枚举与 \(l\) 搭配的数 \(k\),删掉它们需要先删掉 \([l+1,k-1]\),然后 \([k+1,r]\) 则可以穿插其中,组合数乘一下即可。

注意预处理 \(f[i+1,i]=1\),这样才能考虑到 \(k=l+1\) 的情况。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 410;
const LL P = 998244353;
int n, m; LL f[N][N], C[N][N]; bool a[N][N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

void Plus(LL &x, LL y) {x = (x + y) % P;}

int main() {
	n = read() << 1, m = read();
	for(int i = 1; i <= m; i ++) {
		int u = read(), v = read();
		a[u][v] = true;
	}
	C[0][0] = 1;
	for(int i = 1; i <= n; i ++) {
		C[i][0] = C[i][i] = 1;
		for(int j = 1; j < i; j ++)
			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
	}
	for(int i = 1; i <= n; i ++) f[i + 1][i] = 1;
	for(int l = n; l >= 1; l --)
		for(int r = l + 1; r <= n; r += 2)
			for(int k = l + 1; k <= r; k += 2)
				if(a[l][k]) Plus(f[l][r], C[(r - l + 1) / 2][(k - l + 1) / 2] * f[l + 1][k - 1] % P * f[k + 1][r] % P);
	printf("%lld\n", f[1][n]);
}

\(G\)

对 \(1\sim n\) 这些数分 \(k\) 组,其中 \(\bmod m\) 同余的不能在同一组,对于 \(k=1\sim n\) 求方案数。

设 \(a_i\) 表示 \(\bmod m=i\) 的数的个数,显然 \(k\geq \max a_i\) 才有解。

一个比较直接的想法时得到 \(f(i)\):

\[f(i)=\prod\limits_{i=0}^{m-1} \binom{k}{c_i}c_i! \]

唯一的问题时可能出现某个集合为空的情况,即 \(f(i)\) 的实际意义为,将所有数填入 \(i\) 个集合的方案数,可能存在空集。

得到 \(f\) 数组,考虑容斥一下,枚举必为空的集合个数为 \(k-i\),则有:

\[g(k)=\sum\limits_{i=0}^k (-1)^{k-i} f(i)\binom{k}{i} \]

正确性还是比较显然的,首先 \(f(k)\),减去至少有一个为空的情况。

发现由于最后组合数的存在,若 \(f(k)\) 中有至少一个空则可能会减多,所以要加回至少有两个空的情况,依此类推...

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 5010;
const LL P = 998244353;
int n, m, a[N];
LL f[N], g[N], fac[N], inv[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

LL Pow(LL a, LL b) {
	LL sum = 1;
	for(; b; b >>= 1) {
		if(b & 1) sum = sum * a % P;
		a = a * a % P;
	}
	return sum;
}

void Pre() {
	int t = N - 10;
	fac[0] = inv[0] = 1;
	for(int i = 1; i <= t; i ++) fac[i] = fac[i - 1] * i % P;
	inv[t] = Pow(fac[t], P - 2);
	for(int i = t - 1; i >= 1; i --) inv[i] = inv[i + 1] * (i + 1) % P;
}

LL C(int n, int m) {
	if(n < m) return 0;
	return fac[n] * inv[n - m] % P * inv[m] % P;
}

int main() {
	Pre();
	n = read(), m = read();
	for(int i = 1; i <= n; i ++) a[i % m] ++;
	for(int i = 1; i <= n; i ++) {
		f[i] = 1;
		for(int j = 0; j < m; j ++)
			f[i] = f[i] * C(i, a[j]) % P * fac[a[j]] % P;
	}
	for(int i = 1; i <= n; i ++) {
		g[i] = 0;
		for(int j = 0; j <= i; j ++)
			if((i - j) & 1)
				g[i] = (g[i] + P - f[j] * C(i, j) % P) % P;
			else
				g[i] = (g[i] + f[j] * C(i, j) % P) % P;
		g[i] = g[i] * inv[i] % P;
		printf("%lld\n", g[i]);
	}
	return 0;
}

标签:AtCoder,const,Beginner,217,int,LL,sum,include,inv
来源: https://www.cnblogs.com/lpf-666/p/15236036.html