Educational Codeforces Round 65
E:
f
(
l
,
r
)
f(l,r)
f(l,r) 的定义为从
a
a
a 中删除所有
l
≤
a
i
≤
r
l \leq a_i \leq r
l≤ai≤r 得到的数列,题意是要求出有多少对
(
l
,
r
)
(l,r)
(l,r) 满足
f
(
l
,
r
)
f(l,r)
f(l,r) 非递减。
设
G
(
p
)
G(p)
G(p) 为删除
a
i
<
p
a_i < p
ai<p 所有数后得到的数列。设
L
(
p
)
L(p)
L(p) 为删除所有
a
i
>
p
a_i>p
ai>p 的所有数后得到的数列。可以看出
f
(
l
,
r
)
=
G
(
l
)
∪
L
(
r
)
f(l,r)=G(l) \cup L(r)
f(l,r)=G(l)∪L(r) 。对于每一个数组可以发现有一个固定的
p
′
p'
p′,当
p
>
p
′
p>p'
p>p′ 时,
G
(
p
)
G(p)
G(p) 才非递减,因此可以通过枚举非递减
G
(
p
)
G(p)
G(p),对每一个
p
p
p 枚举一个最大的
k
k
k,使得
F
(
k
)
F(k)
F(k) 插入到
G
(
p
)
G(p)
G(p) 后整个数列非递减。将所有结果求和即为答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
vector<int> num[N];
int a[N];
int main() {
int n, x;
scanf("%d%d", &n, &x);
for(int i=1; i<=n; ++i) {
int k;
scanf("%d", &k);
num[k].push_back(i);
}
int nl, nr;
int mx=0;
memset(a, 0, sizeof(a));
for(nl=1; nl<=x;++nl) {
if(num[nl].size()) {
bool ok = true;
int tmp = mx;
for(int k : num[nl]) {
if(k<mx) ok = false;
tmp = max(tmp, k);
}
if(!ok) break;
for(int k : num[nl]) {
a[k] = nl;
}
mx = tmp;
}
}
--nl;
int mn=n+1;
for(nr=x;nr>=1;--nr) {
if(num[nr].size()) {
bool ok = true;
int tmp = mn;
for(int k : num[nr]) {
if(k>mn) ok = false;
tmp = min(tmp, k);
}
if(!ok) break;
mn = tmp;
}
}
++nr;
// cout << nl << " " << nr << endl;
int l = min(nl,x-1); // 1 2 3 4 5
int p = n;
long long ans = l+1;
for(int i=x;i>=nr;--i) {
if(num[i].size()) { // i表示选取[i,x]有序序列,截掉的最大串应该为[1, i-1]
int mn = N;
for(int k : num[i]) {
mn=min(mn, k);
}
for(int i=mn; i<=p; ++i) {
if(a[i]) l = min(l, a[i]-1); //删掉一些冲突的。
}
p=mn-1;
}
ans += min(l+1, i-1);
}
printf("%I64d\n", ans);
}
F:
通过观察可以发现
f
(
l
,
r
)
=
∑
i
=
l
r
a
i
×
(
l
o
w
e
r
(
l
,
r
,
a
i
)
+
1
)
f(l,r)=\sum_{i=l}^{r}a_i \times (lower(l, r,a_i)+1)
f(l,r)=i=l∑rai×(lower(l,r,ai)+1)
其中
l
o
w
e
r
(
l
,
r
,
x
)
lower(l,r,x)
lower(l,r,x) 为在区间
[
l
,
r
]
[l,r]
[l,r]内小于
x
x
x 的数的数量。然后经过一阵推导可以将问题转化为枚举所有
a
i
a_i
ai ,对lower求和的问题,对原问题进行了简化。解决这个问题的关键是能想到把枚举
a
i
a_i
ai 的求和式提到最外面来,从而转化为枚举
a
i
a_i
ai 计算的问题,简化了复杂度。
具体推导
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
using ll = long long;
const ll mod = 1e9+7;
int n;
ll c[N];
ll L[N], R[N];
int a[N];
int b[N], rk[N];
int lowbit(int x) {
return x&-x;
}
void update(int x, ll v) {
for(; x<=n; x+=lowbit(x)) c[x]+=v;
}
ll query(int x) {
ll res = 0;
for(; x>0; x-=lowbit(x)) res+=c[x];
return res%mod;
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
for(int i=1; i<=n; ++i) b[i]=i;
sort(b+1, b+1+n, [](int l, int r){ return a[l]<a[r]; });
for(int i=1; i<=n; ++i) rk[b[i]]=i;
for(int i=1; i<=n; ++i) {
L[i] = query(rk[i]);
// cout << L[i] << endl;
update(rk[i], i);
}
memset(c, 0, sizeof(c));
for(int i=n; i>=1; --i) {
R[i] = query(rk[i]);
update(rk[i], n-i+1);
}
// for(int i=1; i<=n; ++i) {
// printf("lr: %I64d %I64d\n", L[i], R[i]);
// }
ll ans = 0;
for(int i=1; i<=n; ++i) {
ans += 1LL*a[i]*(((n-i+1)*L[i]%mod+i*R[i]%mod)%mod)%mod;
ans %= mod;
ans += 1LL*a[i]*(n-i+1)%mod*i%mod;
ans%=mod;
// cout << ans << endl;
}
printf("%I64d\n", ans);
}
G:
将碰撞看成两类:块与块的碰撞和块与线的碰撞,从而能进行分类讨论。
对于块与块的碰撞,可以观察到当且仅当两个块距离对折点的距离小于等于1时才会碰撞,且离对折点最近的块与块碰撞最先发生。因此可以枚举对折点两端块分布的bitset,按位与用 _Find_first
找到第一个碰撞点。不需要维护所有碰撞点,因为题目有块之间最大距离为d的限制,可以保证仅仅枚举左右两端各长为d的bitset,可以保证得到的最小弧度是
a
r
c
t
a
n
2
d
arctan\frac{2}{d}
arctand2 ,bitset范围之外就算碰撞,最大弧度也只有
a
r
c
t
a
n
2
d
arctan \frac{2}{d}
arctand2 。
对于线与块的碰撞,分别找左右两边第一个块就行。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
using ll = long long;
const ll mod = 1e9+7;
int n;
ll c[N];
ll L[N], R[N];
int a[N];
int b[N], rk[N];
int lowbit(int x) {
return x&-x;
}
void update(int x, ll v) {
for(; x<=n; x+=lowbit(x)) c[x]+=v;
}
ll query(int x) {
ll res = 0;
for(; x>0; x-=lowbit(x)) res+=c[x];
return res%mod;
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; ++i) scanf("%d", &a[i]);
for(int i=1; i<=n; ++i) b[i]=i;
sort(b+1, b+1+n, [](int l, int r){ return a[l]<a[r]; });
for(int i=1; i<=n; ++i) rk[b[i]]=i;
for(int i=1; i<=n; ++i) {
L[i] = query(rk[i]);
// cout << L[i] << endl;
update(rk[i], i);
}
memset(c, 0, sizeof(c));
for(int i=n; i>=1; --i) {
R[i] = query(rk[i]);
update(rk[i], n-i+1);
}
// for(int i=1; i<=n; ++i) {
// printf("lr: %I64d %I64d\n", L[i], R[i]);
// }
ll ans = 0;
for(int i=1; i<=n; ++i) {
ans += 1LL*a[i]*(((n-i+1)*L[i]%mod+i*R[i]%mod)%mod)%mod;
ans %= mod;
ans += 1LL*a[i]*(n-i+1)%mod*i%mod;
ans%=mod;
// cout << ans << endl;
}
printf("%I64d\n", ans);
}