ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AtCoder Beginner Contest 217

2021-09-06 22:35:38  阅读:247  来源: 互联网

标签:AtCoder const Beginner 217 int LL sum include inv


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有