1013号模拟赛复盘

预期分数 100 + 48 + 20 + 15 = 183
实际分数 100 + 15 + 20 + 0 = 135

挂分

收获or反思?

bitset的查找1的个数的复杂度为O(num1)
有些操作能不用stl就不要用stl, (stl)伤透了

至少分几段:贪心

在这里插入图片描述
在这里插入图片描述
首先第一个元素一定是某个子序列中的最小值,考虑它作为覆盖的最小值的 r r r, 在 [ 1 , r ] [1,r] [1,r]中选出一个最远的最大值,则一定优秀,接下来在 [ r + 1 , n ] [r + 1, n] [r+1,n]的区间内接着上述操作即可,证明考虑反证法
复杂度 O ( n l o g n ) O(nlog_n) O(nlogn), 有更优秀的 O ( n ) O(n) O(n)做法

#include<bits/stdc++.h>
using namespace std;
#define LL long long 
const int MAX = 1e6 + 70;
int n, a[MAX], ans = 0;
int minn[MAX]; //表示以i为开头最小所能到的最右边 
stack<int> s; 
struct SegmentTree {
	int l, r, maxx, id;
	#define l(x) tree[x].l
	#define r(x) tree[x].r
	#define maxx(x) tree[x].maxx
	#define id(x) tree[x].id
}tree[MAX * 4];
struct node {
	int maxx, id;
};
void update(int p) {
	if(maxx(2 * p) > maxx(2 * p + 1)) {
		maxx(p) = maxx(2 * p);
		id(p) = id(2 * p);
	} else {
		maxx(p) = maxx(2 * p + 1);
		id(p) = id(2 * p + 1);
	}
}
void build(int p, int l, int r) {
	l(p) = l, r(p) = r;
	if(l == r) {
		maxx(p) = a[l];
		id(p) = l;
		return ;
	}
	int mid = (l + r) >> 1;
	build(2 * p, l, mid);
	build(2 * p + 1, mid + 1, r);
	update(p);
}
node New(node X, node Y) {
	if(Y.maxx > X.maxx) {
		return Y;
	}
	else if(X.maxx > Y.maxx){
		return X;
	}
	else if(X.maxx == Y.maxx) {
		if(X.id > Y.id) return X;
		else return Y;
	}
}
node Find(int p, int l, int r) {
	if(l(p) >= l && r(p) <= r) {
		return (node){maxx(p), id(p)};
	}
	int mid = (l(p) + r(p)) >> 1;
	node res; res.maxx = 0;
	if(r > mid) {
		node Now = Find(2 * p + 1, l, r);
		res = New(res, Now);
	}
	if(l <= mid) {
		node Now = Find(2 * p, l, r);
		res = New(res, Now);
	}  
	return res;
}
void work(int l, int r) {
	if(l > n) {
		return ;
	}
	int R = minn[l];
	int i = R;
	node Now = Find(1, l, R);
	ans += 1;
	work(Now.id + 1, r);	
}
int main() {	
//	freopen("sample2_test53.in","r",stdin);
//	freopen("mine.out","w",stdout);
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	build(1, 1, n);
	for(int i = 1; i <= n; i++) {
		while(!s.empty() && a[i] < a[s.top()]) {
			int now = s.top(); s.pop();
			minn[now] = i - 1;
		}
		s.push(i);
	}
	while(!s.empty()) {
		int now = s.top(); s.pop();
		minn[now] = n;
	} 
	work(1, n);
	printf("%d\n", ans);
	return 0;
}

交换消消乐

在这里插入图片描述
在这里插入图片描述
将贡献拆成两部分,一部分为消除贡献显然为 n n n,另一部分为移动贡献

考虑对于一个元素 i i i,把 i i i消掉需要多少步

i i i左右端点分别为 l i , r i l_i,r_i li,ri

若只将 [ l i , r i ] [l_i,r_i] [li,ri]中元素移除区间,不考虑移动左右端点

如果将 i i i这个元素消掉,则需要 [ l i , r i ] [l_i,r_i] [li,ri]中的出现次数为奇数的元素离开 [ l i , r i ] [l_i,r_i] [li,ri]的区间

通过打表或手玩发现,移动次数为所有 [ l i , r i ] [l_i,r_i] [li,ri]中出现奇数次的数的个数之和

这样我们就得到了一个 n 2 n ^ 2 n2做法,发现统计奇数次出现数目不好统计,正难则反

r i − l i + 1 − 2 ∗ n u m m o d 2 = 0 r_i-l_i+1-2*num_{mod2=0} rili+12nummod2=0含义是区间长度减去出现偶数次的数的个数 ∗ 2 *2 2即为出现奇数次个数

发现满足 l i < = l j l_i<=l_j li<=lj r i > = r j r_i>=r_j ri>=rj二维数点问题,树状数组维护即可

#include<bits/stdc++.h>
using namespace std;
#define LL long long 
const int MAX = 5e5 + 70;
int n,val[2 * MAX];
LL ans = 0, tree[MAX * 2];
bool bol[MAX];
struct made {
	int l, r;
}a[MAX];
bool mycmp(made X, made Y) { return X.l > Y.l; }
int lowbit(int x) { return x & (-x); }
LL Find(int x) {
	LL res = 0;
	for(int i = x; i; i -= lowbit(i)) res += tree[i];
	return res;
}
void add(int x) {
	for(int i = x; i <= 2 * n; i += lowbit(i)) tree[i] += 1;
}
int main() {
	scanf("%d", &n);
	for(int i = 1; i <= 2 * n; i++) {
		scanf("%d", &val[i]);
		if(bol[val[i]] == 0) {
			a[val[i]].l = i;
			bol[val[i]] = 1;
		} else a[val[i]].r = i;
	}
	sort(a + 1, a + 1 + n, mycmp);
	for(int i = 1; i <= n; i++) {
		LL res = Find(a[i].r);
		ans = ans + (a[i].r - a[i].l - 1 - 2 * res);
		add(a[i].r);
	}
	ans = ans / 2;
	ans = ans + n;
	printf("%lld\n", ans);
	return 0;
}

[ABC232H] King’s Tour:构造,

Road of the King

在这里插入图片描述
神奇 D P DP DP,希望得到一个 n 3 n^3 n3的做法

首先发现 1 1 1能到达所有点, 所以若一个图为强联通分量,当且仅当所有点都能到达 1 1 1

不妨设计 D P DP DP状态为 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示已经走了 i i i步,且经过了 j j j个点,能够到达 1 1 1点的个数为 k k k的方案数

我们想初始值如何赋,根据状态可得 f [ 0 ] [ 1 ] [ 1 ] = 1 f[0][1][1] = 1 f[0][1][1]=1

转移分三种情况

1.若下一步前往了一个新的节点,得到转移 : f [ i + 1 ] [ j + 1 ] [ k ] + = f [ i ] [ j ] [ k ] ∗ ( n − j ) f[i + 1][j + 1][k] += f[i][j][k] *(n-j) f[i+1][j+1][k]+=f[i][j][k](nj)
2.若下一步重新去往了无法到达 1 1 1的节点, 得到转移 f [ i + 1 ] [ j ] [ k ] = f [ i ] [ j ] [ k ] ∗ ( j − k ) f[i + 1][j][k]=f[i][j][k]*(j-k) f[i+1][j][k]=f[i][j][k](jk)
3.若下一步去往了任意一个可以到达 1 1 1的节点,则可以使所有不能到达 1 1 1的节点全部变为可以到达, 得到转移 f [ i + 1 ] [ j ] [ j ] = f [ i ] [ j ] [ k ] ∗ k f[i+1][j][j]=f[i][j][k]*k f[i+1][j][j]=f[i][j][k]k
综上即可解决问题

题目的分析其实非常巧妙,为何这样设计状态,为何这样转移一定是正确的

都可以从题目要求每次都从当前节点指向下一节点,起点为 1 1 1 这两个限制条件,或者关键性质得出,所以要多注意题目的限制条件,设计与限制条件有关的 D P DP DP状态去解决问题

posted @ 2023-10-19 20:38  Nogtade  阅读(3)  评论(0编辑  收藏  举报  来源