Rank
![](https://cdn.jsdelivr.net/gh/xiaok0707/MyPhotos/img/20201021124628.png)
这次的 C、D 都有难度,比赛时没有好的思路.
A. 删除某些元素后的数组均值
简单模拟.
class Solution {
public:
double trimMean(vector<int>& arr) {
int n=arr.size();
sort(arr.begin(),arr.end());
double ans=0;
int sz=n/20;
for(int i=sz;i+sz<n;++i) ans+=arr[i];
ans/=1.0*(n-sz-sz);
return ans;
}
};
B. 网络信号最好的坐标
暴力枚举二维空间中输入范围内的所有点,计算最大值.
class Solution {
public:
vector<int> bestCoordinate(vector<vector<int>>& towers, int radius) {
int n=towers.size();
int minx=INT_MAX,maxx=0;
int miny=INT_MAX,maxy=0;
for(auto t:towers){
int x=t[0],y=t[1];
minx=min(x,minx);
maxx=max(x,maxx);
miny=min(y,miny);
maxy=max(y,maxy);
}
int rx,ry,ans=0;
for(int x=minx;x<=maxx;++x){
for(int y=miny;y<=maxy;++y){
int sum=0;
for(auto t:towers){
int tx=t[0];
int ty=t[1];
int q=t[2];
int d2=(x-tx)*(x-tx)+(y-ty)*(y-ty);
if(d2>radius*radius) continue;
sum+=floor(1.0*q/(1.0+sqrt(1.0*d2)));
}
if(sum>ans){
ans=sum;
rx=x;
ry=y;
}
}
}
return {rx,ry};
}
};
C. 大小为 K 的不重叠线段的数目
解法一:dp.
令 dp[i][j][0]
和 dp[i][j][1]
分别表示前 i
个点选择 j
个互不相交的线段 不使用/使用 最后一个点的合法方案数. 如果使用最后一个点,表示最后一个区间的右端点是第 i
个点. 递推方程如下:
dp[i][j][0]
比较好分析,不使用 第 i
个点,那么只能在前 i-1
个点中选择 j
个区间,因此:
d
p
[
i
]
[
j
]
[
0
]
=
d
p
[
i
−
1
]
[
j
]
[
0
]
+
d
p
[
i
−
1
]
[
j
]
[
1
]
dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1]
dp[i][j][0]=dp[i−1][j][0]+dp[i−1][j][1]
dp[i][j][1]
应当这样考虑,最后一个区间的长度如果为 1,则应当在前 i-1
个点中选出 j-1
个点,即:
d
p
[
i
−
1
]
[
j
−
1
]
[
0
]
+
d
p
[
i
−
1
]
[
j
−
1
]
[
1
]
dp[i-1][j-1][0]+dp[i-1][j-1][1]
dp[i−1][j−1][0]+dp[i−1][j−1][1]
如果最后一个区间的长度大于 1,则对于前 i-1
个点来说,仍然相当于从中取出 j
个区间,且最后一个区间的右端点是第 i-1
个点,即:
d
p
[
i
−
1
]
[
j
]
[
1
]
dp[i-1][j][1]
dp[i−1][j][1]
因此,dp[i][j][1]
为上述二者之和:
d
p
[
i
]
[
j
]
[
1
]
=
d
p
[
i
−
1
]
[
j
−
1
]
[
0
]
+
d
p
[
i
−
1
]
[
j
−
1
]
[
1
]
+
d
p
[
i
]
[
j
−
1
]
[
1
]
dp[i][j][1]=dp[i-1][j-1][0]+dp[i-1][j-1][1]+dp[i][j-1][1]
dp[i][j][1]=dp[i−1][j−1][0]+dp[i−1][j−1][1]+dp[i][j−1][1]
初始状态为
d
p
[
0
]
[
0
]
[
0
]
=
1
dp[0][0][0]=1
dp[0][0][0]=1 .
class Solution {
public:
constexpr static int maxn=1005;
constexpr static int mod=1e9+7;
int dp[maxn][maxn][2];
int numberOfSets(int n, int k) {
dp[0][0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<i;++j){
dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1];
dp[i][j][0]%=mod;
dp[i][j][1]=dp[i-1][j][1];
if(j-1>=0){
dp[i][j][1]+=dp[i-1][j-1][0];
dp[i][j][1]%=mod;
dp[i][j][1]+=dp[i-1][j-1][1];
dp[i][j][1]%=mod;
}
}
}
return (dp[n][k][0]+dp[n][k][1])%mod;
}
};
解法二:组合数学.
题目问满足 0 ≤ l 1 < r 1 ≤ l 2 < r 2 . . . ≤ l k < r k < n 0 \leq l_1 < r_1 \leq l_2<r_2 ... \leq l_k<r_k<n 0≤l1<r1≤l2<r2...≤lk<rk<n 这个式子的 ( l 1 , r 1 , l 2 , r 2 . . . l k , r k ) (l_1,r_1,l_2,r_2...l_k,r_k) (l1,r1,l2,r2...lk,rk) 的个数.
做数学变换,令
l
i
′
=
l
i
+
i
−
1
,
r
i
′
=
r
i
+
i
−
1
l_i'=l_i+i-1,r_i'=r_i+i-1
li′=li+i−1,ri′=ri+i−1. 问题变成了求解满足
0
≤
l
1
′
<
r
1
′
<
l
2
′
<
r
2
′
.
.
.
<
l
k
′
<
r
k
′
<
n
+
k
−
1
0 \leq l_1' < r_1' <l_2'<r_2' ... < l_k'<r_k'<n+k-1
0≤l1′<r1′<l2′<r2′...<lk′<rk′<n+k−1
这个式子的
(
l
1
′
,
r
1
′
,
l
2
′
,
r
2
′
.
.
.
l
k
′
,
r
k
′
)
(l_1',r_1',l_2',r_2'...l_k',r_k')
(l1′,r1′,l2′,r2′...lk′,rk′) 的个数.
相当于从 n + k − 1 n+k-1 n+k−1 个互不相同的元素中取出 2 k 2k 2k 个数,即 a n s = C n + k − 1 2 k ans=C^{2k}_{n+k-1} ans=Cn+k−12k.
class Solution:
def numberOfSets(self, n: int, k: int) -> int:
return math.comb(n+k-1,k*2) % (10**9+7)
D. 奇妙序列
解法一:线段树.
用线段树维护区间和,同时支持区间加法和区间乘法,需要两个 lazy
标记 add
和 mul
分别对应加操作和乘操作. 对于线段树上的某个结点 x
,我们规定计算顺序是先乘后加即最终的值应该是 x*mul+add
;所以如果遇到了先加后乘的情况,在下推乘法标记 mul
的时候会影响到子节点的 add
标记,子节点的实际值变成 (x+add)*mul=x*mul+add*mul
,所以要把子节点的 add
标记乘上当前结点的 mul
,才能保证向下的正确性.
namespace SegTree{
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
const int maxn=1e5+50;
const int mod=1e9+7;
typedef long long ll;
struct Tree{
int le,ri,len;
ll sum,add,mul;
}tree[maxn<<2];
void build(int id,int le,int ri){
node.le=le;
node.ri=ri;
node.len=ri-le+1;
node.sum=0;
node.add=0;
node.mul=1;
if(le==ri) return;
int mid=le+ri>>1;
build(id<<1,le,mid);
build(id<<1|1,mid+1,ri);
}
void pushup(int id){
node.sum=lson.sum+rson.sum;
node.sum%=mod;
}
void add(int id,ll val){
node.sum=(node.sum+val*node.len)%mod;
node.add=(node.add+val)%mod;
}
void mul(int id,ll val){
node.sum=node.sum*val%mod;
node.add=node.add*val%mod;
node.mul=node.mul*val%mod;
}
void pushdown(int id){
mul(id<<1,node.mul);
add(id<<1,node.add);
mul(id<<1|1,node.mul);
add(id<<1|1,node.add);
node.add=0;
node.mul=1;
}
void update_add(int id,int le,int ri,ll val){
if(node.le==le && node.ri==ri){
add(id,val);
return;
}
pushdown(id);
int mid=node.le+node.ri>>1;
if(ri<=mid) update_add(id<<1,le,ri,val);
else if(le>mid) update_add(id<<1|1,le,ri,val);
else{
update_add(id<<1,le,mid,val);
update_add(id<<1|1,mid+1,ri,val);
}
pushup(id);
}
void update_mul(int id,int le,int ri,ll val){
if(node.le==le && node.ri==ri){
mul(id,val);
return;
}
pushdown(id);
int mid=node.le+node.ri>>1;
if(ri<=mid) update_mul(id<<1,le,ri,val);
else if(le>mid) update_mul(id<<1|1,le,ri,val);
else{
update_mul(id<<1,le,mid,val);
update_mul(id<<1|1,mid+1,ri,val);
}
pushup(id);
}
int query(int id,int le,int ri){
if(node.le==le && node.ri==ri){
return node.sum;
}
pushdown(id);
int mid=node.le+node.ri>>1;
if(ri<=mid) return query(id<<1,le,ri);
else if(le>mid) return query(id<<1|1,le,ri);
else return (query(id<<1,le,mid)+query(id<<1|1,mid+1,ri))%mod;
}
}
class Fancy {
public:
int tot,n=1e5;
Fancy() {
tot=0;
SegTree::build(1,1,n);
}
void append(int val) {
++tot;
SegTree::update_add(1,tot,tot,val);
}
void addAll(int inc) {
SegTree::update_add(1,1,tot,inc);
}
void multAll(int m) {
SegTree::update_mul(1,1,tot,m);
}
int getIndex(int idx) {
if(++idx>tot) return -1;
return SegTree::query(1,idx,idx);
}
};
解法二:数论.
所有的 addAll
和 multAll
操作都浓缩为 (a,b)
表示将 x
变换成 ax+b
. 最初 a=1,b=0
.
- 执行
addAll(inc)
操作,就将b+=inc
. - 执行
multAll(m)
操作,就将a
和b
同时乘m
.
我们维护这样的 2 个序列 a
和 b
以及初始值序列 v
,在遇到 getIndex(idx)
时,
(
a
_
i
d
x
,
b
_
i
d
x
)
(a\_{idx},b\_{idx})
(a_idx,b_idx) 表示在
v
_
i
d
x
v\_{idx}
v_idx 被加入之前,将所有操作浓缩后的结果,此时序列的最后一项
(
a
,
b
)
(a,b)
(a,b) 表示直到目前为止所有操作浓缩后的结果.
也就是说,对
v
_
i
d
x
v\_{idx}
v_idx 进行的操作就相当于把
(
a
_
i
d
x
,
b
_
i
d
x
)
(a\_{idx},b\_{idx})
(a_idx,b_idx) 变成
(
a
,
b
)
(a,b)
(a,b) 的操作,记为
(
a
0
,
b
0
)
(a_0,b_0)
(a0,b0).
a
i
d
x
×
a
0
≡
a
(
m
o
d
m
)
b
i
d
x
×
a
0
+
b
0
≡
b
(
m
o
d
m
)
a_{idx}×a_0 \equiv a \ (mod \ m) \\ b_{idx}×a_0+b_0 \equiv b \ (mod \ m)
aidx×a0≡a (mod m)bidx×a0+b0≡b (mod m)
通过求解线性同余方程组,即可得到
(
a
0
,
b
0
)
(a_0,b_0)
(a0,b0) 的值.
class Fancy {
private:
constexpr static int mod=1e9+7;
vector<int> v,a,b;
public:
Fancy() {
v.clear();
a.clear();
b.clear();
a.emplace_back(1);
b.emplace_back(0);
}
int pw(int x,int n){
int ans=1;
while(n){
if(n&1) ans=1LL*ans*x%mod;
x=1LL*x*x%mod;
n>>=1;
}
return ans;
}
void append(int val) {
v.emplace_back(val);
a.emplace_back(a.back());
b.emplace_back(b.back());
}
void addAll(int inc) {
b.back()=b.back()+inc;
b.back()%=mod;
}
void multAll(int m) {
b.back()=1LL*b.back()*m%mod;
a.back()=1LL*a.back()*m%mod;
}
int getIndex(int idx) {
if(idx>=(int)v.size()) return -1;
int a0=1LL*pw(a[idx],mod-2)*a.back()%mod;
int b0=(b.back()-1LL*b[idx]*a0%mod+mod)%mod;
return (1LL*a0*v[idx]+b0)%mod;
}
};