Educational Codeforces Round 66
E:
题意是给定
n
n
n 个区间,再给定
m
m
m 个区间,问这
m
m
m 个区间中的每一个至少需要多少个从
n
n
n 个区间内取出来的区间使得完全覆盖所有点。
设 dp[i][j]
为从
i
i
i 左边的任意位置,经过
2
j
2^j
2j 个区间最远可以跳到的位置。转移就是 dp[i][j+1]=dp[dp[i][j]][j]
。
那么对于每一个询问 [l,r]
,就通过
d
p
dp
dp 算出要跳的区间的最小值。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
const int B = 19;
int dp[N][B];
// dp[i][j] 表示从i点左边任一位置,经过2^j个区间跳跃,
// 最远可以跳到的位置。
// 转移:dp[i][j+1]=dp[dp[i][j]][j]
int main() {
int n, m;
scanf("%d%d", &n, &m);
memset(dp, -1, sizeof(dp));
for(int i=0; i<n; ++i) {
int l, r;
scanf("%d%d", &l, &r);
dp[l][0]=max(dp[l][0], r);
}
for(int i=1; i<N; ++i) {
dp[i][0] = max(dp[i-1][0], dp[i][0]);
}
// printf("dd: %d\n", dp[8][0]);
for(int j=1; j<B; ++j) {
for(int i=0; i<N; ++i) {
if(dp[i][j-1]!=-1) {
dp[i][j] = max(dp[i][j], dp[dp[i][j-1]][j-1]);
}
}
}
for(int i=0; i<m; ++i) {
int l, r;
scanf("%d%d", &l, &r);
int ans = 1;
// printf("dp: %d\n", dp[l][0]);
for(int j=B-1; j>=0; --j) {
if(dp[l][j]<r&&dp[l][j]!=-1) {
l = dp[l][j];
ans+=(1<<j);
}
}
// printf("l: %d\n", l);
if(dp[l][0]<r) ans=-1;
printf("%d\n", ans);
}
}
F:
题意是给定一个数组,问有多少个子段可以形成一个排列。可以枚举所有点,设这个点为排列中的最大值,然后剖出一个包含这个点的最长的区间使得区间内除了这个点外所有点都小于这个点。
如 1 2 3 1 4 7 5 6 2 1
对于 7
这个点剖出的是整个区间。问题转化为剖出的这个区间有多少个最大值为 7
的排列。
首先可以算出每个点最远可以向左扩展到的位置,使得扩展出的区间内没有任何一个重复元素。那么可以从大到小地枚举原数组中的所有点,对于一个点 a[k]
维护这样一棵线段树:线段树中如果 i
点扩展长度大于 a[k]
,那么 i
的权值+1 。这样就可以求剖出区间树中某个子区间的权值和,这代表着这一段区间里向左扩展能覆盖到 a[k]
,且扩展长度大于等于 a[k]
,且扩展区间内元素两两不等,这样就形成了一个长度为 a[k]
的排列。
问题的关键是要想出能枚举排列的最大值和通过向左扩展最长两两不等元素区间来算出排列总数。
#include <vector>
#include <stack>
#include <cstdio>
#include <algorithm>
using namespace std;
// 1 2 3 1 4 7 5 6 2 1
// 枚举每一个点作为子排列中的最大点
// 剖出一个最大区间使得除了这个点之外所有元素小于这个点
// 一个点对长为k的排列有贡献,当前仅当其能向左至少延伸k个元素
// 使得这些元素两两不等。
// 求出每个点的最大延伸值,用线段树维护即可。
const int N = 3e5+7;
int a[N], mnt[N], last[N], b[N], left[N], right[N], c[N], n;
vector<int> rext[N];
int lowbit(int x) {
return x&-x;
}
void update(int x, int v) {
for(;x<=n;x+=lowbit(x)) {
c[x]+=v;
}
}
int query(int x) {
int res=0;
for(;x>0;x-=lowbit(x)) {
res+=c[x];
}
return res;
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; ++i) {
scanf("%d", &a[i]);
mnt[i] = max(mnt[i-1], last[a[i]]+1);
last[a[i]]=i;
rext[i-mnt[i]+1].push_back(i);
// printf("rtext: %d %d\n", i-mnt[i]+1, i);
b[i]=i;
}
sort(b+1, b+1+n, [](int x, int y){ return a[x]>a[y]; });
stack<int> s;
for(int i=1; i<=n; ++i) {
while(!s.empty()&&a[i]>a[s.top()]) s.pop();
if(s.empty()) left[i]=1;
else left[i]=s.top()+1;
s.push(i);
}
s = stack<int>();
for(int i=n; i>=1; --i) {
while(!s.empty()&&a[i]>a[s.top()]) s.pop();
if(s.empty()) right[i]=n;
else right[i]=s.top()-1;
s.push(i);
}
// for(int i=1; i<=n; ++i) {
// printf("left: %d, right: %d\n", left[i], right[i]);
// }
int m = n;
long long ans=0;
// for(int i=1; i<=n; ++i) {
// printf("a: %d\n", a[i]);
// }
for(int i=1; i<=n; ++i) {
int k = b[i];
// printf("iter: %d %d\n", k, a[k]);
while(m>=a[k]) {
for(int p : rext[m]) {
update(p, 1);
}
--m;
}
int lb = max(k, left[k]+a[k]-1);
int rb = min(right[k], k+a[k]-1);
if(rb<lb) continue;
int res = query(rb) - query(lb-1);
// printf("%d %d %d %d\n", k, lb, rb, res);
ans += res;
}
printf("%I64d\n", ans);
}