NOI.AC模拟赛第五场

版权声明:本文是蒟蒻写的,转载。。。随便吧 https://blog.csdn.net/xgc_woker/article/details/82812945

这场比赛出的还行吧,挺可做的。


T1:
长度为n+1的序列,其中的每个数都是不大于n的正整数,且n以内每个数至少出现一次,求对于每一个K,本质不同子序列有多少个。


你先不考虑重复,每个K就是:C(n+1,K)。
你找出相同的位置:l1,l2。
那么会出现重复的就是:C(n + l1 - l2,K-1)然后即可。。。


#include <cstdio>
#include <cstring>

using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch =getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

int a[110000];
LL jc[110000], inv[110000];

LL pow_mod(LL a, int k) {
	LL ans = 1;
	while(k) {
		if(k & 1) (ans = ans * a) %= mod;
		(a *= a) %= mod, k /= 2;
	} return ans;
}

LL C(int n, int m) {
	if(m > n) return 0;
	return jc[n] * inv[m] % mod * inv[n - m] % mod;
}

int main() {
	int n = read();
	int l1, l2;
	jc[0] = inv[0] = 1;
	for(int i = 1; i <= n + 1; i++) jc[i] = jc[i - 1] * i % mod;
	inv[n + 1] = pow_mod(jc[n + 1], mod - 2);
	for(int i = n; i >= 1; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
	for(int i = 1; i <= n + 1; i++) {
		int x = read();
		if(a[x]) l1 = a[x], l2 = i;
		a[x] = i;
	}
	for(int i = 1; i <= n + 1; i++) {
		LL u1 = C(n + 1, i);
		LL u2 = C(l1 + n - l2, i - 1);
		printf("%lld\n", (u1 - u2 + mod) % mod);
	}
	return 0;
}

T2:
一个长度为n的序列A,从中删去恰好K个元素,每删一个元素就往左,记cnt为Ai=i的个数,求cnt的最大值。


其实这道题跟[POI2007]堆积木Klo很像,只是多了一个限制条件。
满足转移的话需要三个条件:
j&lt;ij &lt; i,a[j]&lt;a[i]a[j] &lt; a[i], ja[j]&lt;=ia[i]j - a[j] &lt;= i - a[i]
其实后两个可以推出第一个。
于是你就排一遍序,求最长上升子序列即可。
然后考虑限制条件,你发现对于每删一个数的本质其实是,他右边的ai-i全部减一,
那么删K次其实相当于你只要控制ai-i的范围在K以内,若不足K你就判断后面是否够你删。。。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch =getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

struct node {
	int x, y, h, id;
} a[1100000];
int n, k, f[1100000], s[1100000];
int q[1100000];

bool cmp1(node a, node b) {return a.y < b.y;}
bool cmp2(node a, node b) {
	if(a.x == b.x) return a.y > b.y;
	return a.x < b.x;
}
int lowbit(int x) {return x & -x;}
void change(int x, int c) {for(int i = x; i <= n; i += lowbit(i)) s[i] = _max(s[i], c);}
int getmax(int x) {int maxx = -999999999; for(int i = x; i; i -= lowbit(i)) maxx = _max(maxx, s[i]); return maxx;}

int main() {
	n = read(), k = read(); n++; a[1].y = 1;
	for(int i = 2; i <= n; i++) a[i].x = read(), a[i].y = a[i].h = i - a[i].x, a[i].id = i - 1, a[i].h--;
	sort(a + 1, a + n + 1, cmp1);
	int id = 0; q[0] = 999999999;
	for(int i = 1; i <= n; i++) {
		if(a[i].y != q[id]) q[++id] = a[i].y;
		a[i].y = id;
	} sort(a + 1, a + n + 1, cmp2);
	memset(s, 128, sizeof(s));
	int ans = 0; change(a[1].y, 0);
	for(int i = 2; i <= n; i++) {
		f[i] = getmax(a[i].y) + 1;
		change(a[i].y, f[i]);
	}
	for(int i = 1; i <= n; i++) {
		if(a[i].h >= 0 && a[i].h <= k) {
			if(n - a[i].id >= k - a[i].h) ans = _max(ans, f[i]);
		}
	} printf("%d\n", ans);
	return 0;
}

T3:
小D梦见了一棵包含n个节点的树,这棵树包含着神秘的能量。具体来讲,对于这棵树中的一个联通块,它的能量为它拥有的节点中编号连续的最长的一段。举例来说,如果一个连通块包含了原树中编号为{1,3,4,5,7,8}的节点,那么编号连续的最长的一段为{3,4,5}。它所具有的能量值为3。

小D想要从这棵树中获得能量,但由于种种原因,小D只能从这棵树中选取一个节点数不大于k的连通块并获得它的能量值,小D想要知道他能取得的最大能量值是多少。

一棵树包含n个节点的树就是有n个点,n−1条边的连通图。


这题你考虑维护一个双指针,使其满足选l~r的连通块小于等于k
然后对于如何求一个连通块的的大小,你可以按照dfs序排一个序,相邻求距,头尾求距,再除二加一即为联通块大小,这个用双指针维护的话,你可以用set维护。。。


#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define It set<int>::iterator
using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch =getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

struct edge {
	int x, y, next;
} e[210000]; int len, last[110000];
int id, sum, K, dfn[110000], bak[110000], dep[110000], fa[20][110000];
set<int>q;

void ins(int x, int y) {
	e[++len].x = x; e[len].y = y;
	e[len].next = last[x]; last[x] = len;
}

void dfs(int x) {
	dfn[x] = ++id, bak[id] = x;
	for(int i = 1; (1 << i) <= dep[x]; i++) fa[i][x] = fa[i - 1][fa[i - 1][x]];
	for(int k = last[x]; k; k = e[k].next) {
		int y = e[k].y;
		if(y != fa[0][x]) {
			fa[0][y] = x;
			dep[y] = dep[x] + 1;
			dfs(y);
		}
	}
}

int LCA(int x, int y) {
	if(dep[x] > dep[y]) swap(x, y);
	int X = x, Y = y;
	for(int i = 18; i >= 0; i--) if(dep[y] - dep[x] >= (1 << i)){
		y = fa[i][y];
	} if(x == y) return dep[Y] - dep[X];
	for(int i = 18; i >= 0; i--) if(fa[i][x] != fa[i][y]){
		x = fa[i][x], y = fa[i][y];
	} return dep[Y] + dep[X] - dep[fa[0][x]] * 2;
}

void ers(int u) {
	It hh = q.lower_bound(dfn[u]), h1 = hh;
	if(h1 == q.begin()) h1 = q.end(), --h1;
	else --h1;
	It h2 = hh; ++h2;
	if(h2 == q.end()) h2 = q.begin();
	int x = *h1, y = *h2;
	sum -= LCA(bak[x], u) + LCA(u, bak[y]) - LCA(bak[x], bak[y]);
	q.erase(dfn[u]);
}

int main() {
	int n = read(); K = read();
	for(int i = 1; i < n; i++) {
		int x = read(), y = read();
		ins(x, y), ins(y, x);
	} dfs(1);
	int st = 1, ans = 1; q.insert(dfn[1]);
	for(int i = 2; i <= n; i++) {
		It h2 = q.lower_bound(dfn[i]);
		if(h2 == q.end()) h2 = q.begin();
		It h1 = h2;
		if(h1 == q.begin()) h1 = q.end(), --h1;
		else --h1;
		int x = *h1, y = *h2;
		sum += LCA(bak[x], i) + LCA(i, bak[y]) - LCA(bak[x], bak[y]);
		q.insert(dfn[i]);
		while(sum / 2 + 1 > K) ers(st++);
		ans = _max(ans, i - st + 1);
	} printf("%d\n", ans);
	return 0;
}
展开阅读全文

没有更多推荐了,返回首页