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} ri−li+1−2∗nummod2=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]∗(n−j)
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]∗(j−k)
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状态去解决问题