【NOIP2018模拟10.27】总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/er111er/article/details/83446321

真是一场养生比赛。
不得不说我识别水题的能力还是比较强的,T3一道裸的主席树秒切了,T2暴力分十分良心,T1暴力只有10分。还是很后悔,这种结论题我总是懒得去推,结果少了别人90,以后还是要保持冷静思考吧。

T1

T1
Constraints
首先你得把题看懂。
对于一个 n n 的排列,它的贡献就是将它交换有序的最少次数。
我们可以设 f i f_i 表示前 i i 个数所有方案的贡献,那么考虑 i i 放在哪一位。
直接放在第 i i 位,无需交换,只用加上 f i 1 f_{i-1}
如果不放在第 i i 位,我们就要把 i i 换到第 i i 位,贡献一次。不放第 i i 位就有 i 1 i-1 种放法,另外 i 1 i-1 个数的排列是 ( i 1 ) ! (i-1)! ,所以一共有 ( i 1 ) ( i 1 ) ! (i-1)(i-1)! 种方案贡献是 1 1 ,此外另外 i 1 i-1 个数也要交换,产生贡献 ( i 1 ) f i 1 (i-1)f_{i-1}

于是得到递推式:
f i = i f i 1 + ( i 1 ) ( i 1 ) ! f_i=if_{i-1}+(i-1)(i-1)!
然后求逆元除以 n ! n! 就行了。
预处理阶乘和 f n f_n ,复杂度 O ( n ) O(n) ,回答每次 O ( 1 ) O(1)

Code:

#include <cstdio>
#include <cstring>
#include <cstdlib>

const int P = 998244353, N = 1e7 + 7;

int pow(int a, int b)
{
	int ret = 1;
	while (b)
	{
		if (b & 1) ret = 1ll * ret * a % P;
		a = 1ll * a * a % P, b >>= 1;
	}
	return ret;
}

int T, n, f[N], fac[N];

int main()
{
	//freopen("inverse.in", "r", stdin);
	//freopen("inverse.out", "w", stdout);
	
	f[1] = 0, fac[1] = 1;
	for (int i = 2; i <= N - 7; i++)
		f[i] = (1ll * i * f[i - 1] % P + 1ll * (i - 1) * fac[i - 1] % P) % P, fac[i] = 1ll * fac[i - 1] * i % P;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &n);
		printf("%d\n", 1ll * f[n] * pow(fac[n], P - 2) % P);
	}

	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2

这题真™暴力。
T2Constraints
数据范围是亮点。

60 % 60\% 的数据(莫名其妙拿了 70 p t s 70pts ,出题人人真好):
考虑到 n n 只有 600 600 k \sum k 只有 10000 10000 ,容易想到一种 O ( n k ) O(n\sum k) 优秀 做法。
首先 O ( n 3 ) O(n^3) 预处理出每个点两两之间的最短路,Then对于每个询问,在桶里标记一下满足条件的点,就OK了。

100 % 100\% 的数据,要用bitset这个自带 1 32 \frac{1}{32} 常数的黑科技。
bitset可以理解为一个每一位只能是0或1的数组,但是因为它经过压位,时空复杂度都是 O ( n / 32 ) O(n/32) ,其中 n n 是数组位数。我们可以利用这个黑科技做这题。
开一个bitset<N> b[N][N],其中b[i][j]存的是与i距离<=j的点集。由于与i距离<=j的点集不太方便求,我们可以先考虑求与i距离=j的点集,然后用前缀和的方式求出与i距离<=j的点集。
然后每次询问把所有点对应的bitset取个并集,集合大小就是答案了。
求最短路的过程用bfs复杂度是 O ( n ( n + m ) ) = O ( n 2 + n m ) O(n(n+m))=O(n^2+nm) 。吸了氧跑的还挺快,总的询问是 k \sum k ,一次询问复杂度是 O ( n / 32 ) O(n/32) ,时间复杂度就是 O ( n k 32 ) O(\frac{n\sum k}{32}) ,空间复杂度是 O ( n 2 32 ) O(\frac{n^2}{32}) ,再加上O2STL的定制优化,就过了。
Tips:由于重边较多,使用vector存边貌似能更快。

Code:真的暴力

#include <bitset>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

const int N = 1e3 + 7;
inline int read()
{
	int x = 0, f = 0;
	char c = getchar();
	for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
	for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
	return f ? -x : x;
}

int n, m, q;

vector<int> edge[N];
void add(int u, int v) { edge[u].push_back(v); }

int head, tail, que[N], d[N];
bitset<N> b[N][N], tmp;

void init()
{
	n = read(), m = read(), q = read();
	for (int i = 1, u, v; i <= m; i++) u = read(), v = read(), add(u, v), add(v, u);
	for (int i = 1; i <= n; i++)
	{
		memset(d, 0, sizeof(d));
		d[i] = 1, head = 1, que[tail = 1] = i;
		while (head <= tail)
		{
			int u = que[head++], siz = edge[u].size();
			for (int i = 0; i < siz; i++)
				if (!d[edge[u][i]])
					d[edge[u][i]] = d[u] + 1, que[++tail] = edge[u][i];
		}
		for (int j = 1; j <= n; j++) if (d[j]) b[i][d[j] - 1][j] = 1;
		for (int j = 1; j <= n; j++) b[i][j] |= b[i][j - 1];
	}
}

void solve()
{
	for (int i = 1; i <= q; i++)
	{
		int k = read();
		tmp.reset();
		for (int j = 1, x, y; j <= k; j++) x = read(), y = read(), tmp |= b[x][y];
		printf("%d\n", tmp.count());
	}
}

int main()
{
	//freopen("input", "r", stdin);
	//freopen("center.in", "r", stdin);
	//freopen("center.out", "w", stdout);

	init();
	solve();

	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3

这题没穿衣服(真裸)。
T3
Constraints
看到区间第 k k 大,首先会想到主席树。可是看到新增与删除操作,就做不了了吗?不,伟大的shehui主义主席树是无所不能的
由于是在序列左端修改,我们可以考虑倒着建主席树,对于删除操作,我们只需要把该位置对应的主席树全部清空掉,由于一棵主席树新增的节点最多就 O ( l o g n ) O(logn) 个,这个操作复杂度是 O ( l o g n ) O(logn) 的。插入操作就新建一棵主席树,查询操作就开个指针记录一下我插入到了哪里,找到实际要查询的区间求行了。

我的清空方法比较暴力,就是树上所有的节点左儿子右儿子都赋为0。最好离散化一下,这样时间空间都会小很多,总复杂度 O ( ( n + q ) l o g 2 ( n + q ) ) O((n+q)log_2(n+q))

Code:

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

const int N = 4e5 + 7, Q = 2e5 + 7;
inline int read()
{
	int x = 0, f = 0;
	char c = getchar();
	for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
	for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
	return f ? -x : x;
}

int n, q, pt, a[N], tmp[N];
int opt[Q], L[Q], R[Q], K[Q];
int len, arr[N];

int tot, root[N], lson[N * 50], rson[N * 50], sum[N * 50];
void clear(int rt, int l, int r, int po)
{
	sum[rt]--; //这句貌似可以不加
	if (l == r) return;
	int mid = l + r >> 1;
	if (po <= mid) clear(lson[rt], l, mid, po);
	else clear(rson[rt], mid + 1, r, po);
	lson[rt] = rson[rt] = 0;
}
void insert(int &rt, int fa, int l, int r, int po)
{
	if (!rt) rt = ++tot;
	sum[rt] = sum[fa] + 1;
	if (l == r) return;
	int mid = l + r >> 1;
	if (po <= mid) rson[rt] = rson[fa], insert(lson[rt], lson[fa], l, mid, po);
	else lson[rt] = lson[fa], insert(rson[rt], rson[fa], mid + 1, r, po);
}
int getkth(int rt, int fa, int l, int r, int k)
{
	if (l == r) return l;
	int mid = l + r >> 1, siz = sum[lson[rt]] - sum[lson[fa]];
	if (k <= siz) return getkth(lson[rt], lson[fa], l, mid, k);
	else return getkth(rson[rt], rson[fa], mid + 1, r, k - siz);
}

void init()
{
	n = read(), q = read(), pt = q;
	for (int i = 1; i <= n; i++) a[i] = read(), arr[++len] = a[i];
	for (int i = 1; i <= q; i++)
	{
		opt[i] = read();
		if (opt[i] == 2) K[i] = read(), arr[++len] = K[i];
		else if (opt[i] == 3) L[i] = read(), R[i] = read(), K[i] = read();
	}
	sort(arr + 1, arr + len + 1);
	len = unique(arr + 1, arr + len + 1) - arr - 1;
}

void solve()
{
	for (int i = n; i >= 1; i--)
	{
		a[i] = lower_bound(arr + 1, arr + len + 1, a[i]) - arr;
		insert(root[i + q], root[i + q + 1], 1, len, a[i]);
	}
	for (int i = 1; i <= n; i++) tmp[i] = a[i], a[i] = 0;
	for (int i = 1; i <= n; i++) a[i + q] = tmp[i];
	for (int i = 1; i <= q; i++)
	{
		if (opt[i] == 1) ++pt, clear(root[pt], 1, len, a[pt]);
		else if (opt[i] == 2)
		{
			K[i] = lower_bound(arr + 1, arr + len + 1, K[i]) - arr;
			insert(root[pt], root[pt + 1], 1, len, K[i]);
			a[pt--] = K[i];
		}
		else
		{
			int po = getkth(root[pt + L[i]], root[pt + R[i] + 1], 1, len, K[i]);
			printf("%d\n", arr[po]);
		}
	}
}

int main()
{
	//freopen("input", "r", stdin);
	//freopen("output", "w", stdout);
	//freopen("pigeon.in", "r", stdin);
	//freopen("pigeon.out", "w", stdout);

	init();
	solve();

	fclose(stdin);
	fclose(stdout);
	return 0;
}

这是这一周最放松的比赛了吧。

猜你喜欢

转载自blog.csdn.net/er111er/article/details/83446321