松鼠聚会
题目简化:求
min
1
≤
i
≤
n
(
∑
j
=
1
n
max
(
∣
x
i
−
x
j
∣
,
∣
y
i
−
y
j
∣
)
)
\min_{1 \leq i\leq n}(\sum_{j=1}^{n}\max(\left\vert x_i-x_j \right\vert,\left\vert y_i-y_j \right\vert))
1≤i≤nmin(j=1∑nmax(∣xi−xj∣,∣yi−yj∣))
m
a
x
max
max 很麻烦,有什么办法可以去掉
m
a
x
max
max 呢?假使转化后的坐标为
x
1
,
y
1
,
x
2
,
y
2
x_1,y_1,x_2 ,y_2
x1,y1,x2,y2 即答案为
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
\left\vert x_1 -x_2 \right\vert+\left\vert y_1 -y_2 \right\vert
∣x1−x2∣+∣y1−y2∣
已知
∣
x
1
−
x
2
∣
=
m
a
x
(
x
1
−
x
2
,
x
2
−
x
1
)
\left\vert x_1 -x_2 \right\vert=max(x_1-x_2,x_2-x_1)
∣x1−x2∣=max(x1−x2,x2−x1)所以
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
=
m
a
x
{
x
1
−
x
2
+
y
1
−
y
2
x
2
−
x
1
−
y
1
+
y
2
x
1
−
x
2
−
y
1
+
y
2
x
2
−
x
1
+
y
1
−
y
2
\left\vert x_1 -x_2 \right\vert+\left\vert y_1 -y_2 \right\vert= max\begin{cases} x_1-x_2+y_1-y_2\\ x_2-x_1-y_1+y_2\\ x_1-x_2-y_1+y_2\\ x_2-x_1+y_1-y_2 \end{cases}
∣x1−x2∣+∣y1−y2∣=max⎩⎪⎪⎪⎨⎪⎪⎪⎧x1−x2+y1−y2x2−x1−y1+y2x1−x2−y1+y2x2−x1+y1−y2再将x_1,y_1放在一起
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
=
m
a
x
{
(
x
1
+
y
1
)
−
(
x
2
+
y
2
)
(
x
2
+
y
2
)
−
(
x
1
+
y
1
)
(
x
1
−
y
1
)
−
(
x
2
−
y
2
)
(
x
2
−
y
2
)
−
(
x
1
−
y
1
)
\left\vert x_1 -x_2 \right\vert+\left\vert y_1 -y_2 \right\vert= max\begin{cases} (x_1+y_1)-(x_2+y_2)\\ (x_2+y_2)-(x_1+y_1)\\ (x_1-y_1)-(x_2-y_2)\\ (x_2-y_2)-(x_1-y_1) \end{cases}
∣x1−x2∣+∣y1−y2∣=max⎩⎪⎪⎪⎨⎪⎪⎪⎧(x1+y1)−(x2+y2)(x2+y2)−(x1+y1)(x1−y1)−(x2−y2)(x2−y2)−(x1−y1)将式子变回本来的样子
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
=
m
a
x
(
∣
(
x
1
+
y
1
)
−
(
x
2
+
y
2
)
∣
,
∣
(
x
1
−
y
1
)
−
(
x
2
−
y
2
)
∣
)
\left\vert x_1 -x_2 \right\vert+\left\vert y_1 -y_2 \right\vert= max(\left\vert(x_1+y_1)-(x_2+y_2) \right\vert,\left\vert(x_1-y_1)-(x_2-y_2) \right\vert)
∣x1−x2∣+∣y1−y2∣=max(∣(x1+y1)−(x2+y2)∣,∣(x1−y1)−(x2−y2)∣)不妨设
x
1
′
=
x
1
+
y
1
,
y
1
′
=
x
1
−
y
1
x_1^\prime=x_1+y_1,y_1^\prime=x_1-y_1
x1′=x1+y1,y1′=x1−y1可得
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
=
m
a
x
(
∣
x
1
′
−
x
2
′
∣
,
∣
y
1
′
−
y
2
′
∣
)
\left\vert x_1 -x_2 \right\vert+\left\vert y_1 -y_2 \right\vert=max(\left\vert x_1^\prime-x_2^\prime \right\vert,\left\vert y_1^\prime-y_2^\prime\right\vert)
∣x1−x2∣+∣y1−y2∣=max(∣x1′−x2′∣,∣y1′−y2′∣)
这不就把
m
a
x
max
max 去掉了吗?又易得
x
1
=
x
1
′
+
y
1
′
2
,
y
1
=
x
1
′
−
y
1
′
2
x_1=\frac{x_1^\prime+y_1^\prime}{2},y_1=\frac{x_1^\prime-y_1^\prime}{2}
x1=2x1′+y1′,y1=2x1′−y1′所以对于每一个输入的
x
,
y
x,y
x,y 我们都可以转化出对应的
x
1
,
y
1
x_1,y_1
x1,y1 ,最后求
min
1
≤
i
≤
n
{
∑
j
=
1
n
∣
x
i
−
x
j
∣
+
∣
y
i
−
y
j
∣
}
\min_{1 \leq i\leq n}\{\sum_{j=1}^{n}\left\vert x_i-x_j \right\vert+\left\vert y_i-y_j \right\vert\}
1≤i≤nmin{j=1∑n∣xi−xj∣+∣yi−yj∣}而对于绝对值
x
,
y
x,y
x,y 互不影响,我们就可以分开求,分别排序用前缀和预处理,就
x
x
x 而言每次二分查找
x
x
x 对应的位置用前缀和就可以轻松计算绝对值的和了。
#include<bits/stdc++.h>
#define FRE freopen("squirrel.in","r",stdin);\
freopen("squirrel.out","w",stdout);
using namespace std;
template<typename T>inline void read(T &x){
T ch=getchar(),xx=1;x=0;
while(!isdigit(ch)) xx=ch=='-'?-1:xx,ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
x*=xx;
}
template<typename T>inline void prt(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) prt(x/10);
putchar(x%10|48);
}
#define N 100005
#define int long long
int X[N],Y[N];
int x[N],y[N];
int Xsum[N],Ysum[N];
int xx,yy;
int ans=LLONG_MAX;
int n;
signed main(){
// FRE
read(n);
for(int i=1;i<=n;++i){
read(xx),read(yy);
x[i]=X[i]=xx+yy,y[i]=Y[i]=xx-yy;
}
sort(X+1,X+n+1),sort(Y+1,Y+n+1);
for(int i=1;i<=n;++i){
Xsum[i]=Xsum[i-1]+X[i];
Ysum[i]=Ysum[i-1]+Y[i];
}
for(int i=1;i<=n;++i){
int px=lower_bound(X+1,X+n+1,x[i])-X;
int py=lower_bound(Y+1,Y+n+1,y[i])-Y;
int rx=px*x[i]-Xsum[px]+Xsum[n]-Xsum[px]-(n-px)*x[i];
int ry=py*y[i]-Ysum[py]+Ysum[n]-Ysum[py]-(n-py)*y[i];
ans=min(ans,rx+ry);
}
prt(ans>>1ll);
}
拯救小矮人
先按身长加手长从小到大排序,很好理解越容易逃的越靠后,为不好逃的留更多机会,这样排序以后就不会出现后面的人先逃出去比前面的人先逃出去更优的情况,但是有可能前面的人
a
a
a 较大
b
b
b 较小,他不逃出去对答案更优。做个 0/1 背包就行了,
F
[
i
]
F[i]
F[i] 表示走i个人后可以取的最大高度,初值
F
[
0
]
=
∑
b
F[0]=\sum b
F[0]=∑b。
转移式
F
j
=
max
1
≤
i
≤
n
,
F
j
−
1
+
b
i
≥
H
{
F
j
−
1
−
a
i
}
F_j=\max_{1\leq i\leq n,F_{j-1}+b_i\geq H}\{F_{j-1}-a_i\}
Fj=1≤i≤n,Fj−1+bi≥Hmax{Fj−1−ai}
#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x){
T ch=getchar(),xx=1;x=0;
while(!isdigit(ch)) xx=ch=='-'?-1:xx,ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
x*=xx;
}
template<typename T>inline void prt(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) prt(x/10);
putchar(x%10|48);
}
#define N 2005
int dp[N],n,h;
struct node{int a,b;}p[N];
bool operator < (node x,node y){return x.a+x.b<y.a+y.b;}
int main() {
read(n);
for(int i=1;i<=n;i++) read(p[i].a),read(p[i].b);
read(h);
sort(p+1,p+n+1);
memset(dp,0xcf,sizeof dp);
dp[0]=0;
for(int i=1;i<=n;i++) dp[0]+=p[i].a;
for(int i=1;i<=n;i++)
for(int j=i;j>=1;j--)
if(dp[j-1]+p[i].b>=h)
dp[j]=max(dp[j],dp[j-1]-p[i].a);
for(int i=n;~i;i--) //~i 是表示 i>=0
if(dp[i]>=0) return prt(i),0;
}
最长上升子序列
这题,怎么说呢,大佬们都用了平衡树求最终序列,再查看题解时,有人说
v
e
c
t
o
r
vector
vector 也可以直接插入求最后的序列而且不会
T
L
E
TLE
TLE。得到最终序列后,重新从1到
n
n
n 挨个插入数,只不过每次插入的位置就是最终位置,然后显然每次插入一个数,答案只可能被这个数更新,我们就可以令一个数组
F
F
F 表示以
i
i
i 结尾的序列有多长,每次查找当前位置前面的数最大的那个,并尝试更新答案,然后把
F
i
F_i
Fi 插入数列里面去就行了。数组
F
i
F_i
Fi 可以用线段树,树状数组,甚至单调队列维护。
这里贴出线段树维护的。
#include<bits/stdc++.h>
#define mid (L+R>>1)
#define pl (p<<1)
#define pr (p<<1|1)
using namespace std;
template<typename T>inline void read(T &x){
T ch=getchar(),xx=1;x=0;
while(!isdigit(ch)) xx=ch=='-'?-1:xx,ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
x*=xx;
}
#define N 100010
int a[N];
int n,ans;
vector<int> p;
struct SegementTree{
int tr[N<<2];
inline void update(int L,int R,int p,int pos,int k){
if(L==R){tr[p]=k;return;}
if(pos<=mid) update(L,mid,pl,pos,k);
else update(mid+1,R,pr,pos,k);
tr[p]=max(tr[pl],tr[pr]); return;
}
inline int ask(int L,int R,int p,int l,int r){
if(L>R || R<l || L>r) return 0;
if(l<=L && R<=r) return tr[p];
return max(ask(L,mid,pl,l,r),ask(mid+1,R,pr,l,r));
}
}Stree;
signed main(){
read(n);
for(int i=1,x;i<=n;++i) read(x),p.insert(p.begin()+x,i);
for(int i=1;i<=n;++i) a[p[i-1]]=i+1;
for(int i=1;i<=n;++i){
int temp=Stree.ask(1,n+1,1,1,a[i]-1)+1;ans=max(ans,temp),Stree.update(1,n+1,1,a[i],temp);
printf("%d\n",ans);
}
}