VP赛中只过了两题的菜狗竟是我自己
A题:
简单记录一下上次开水闸的时间,判一判就好。
#include <bits/stdc++.h>
using namespace std;
int n, T;
int a[200010];
int sth[500010], scnt;
int main() {
scanf("%d%d", &n, &T);
for(int i=1;i<=n;++i)
scanf("%d", &a[i]);
int las = 0;
sth[++scnt] = las;
a[++n] = 2100000000;
for(int i=2;i<=n;++i) {
if(las + T < a[i])
sth[++scnt] = las + T, sth[++scnt] = a[i], las = a[i];
else
las = a[i];
}
int ans = 0;
for(int i=2;i<=scnt;i+=2)
ans += sth[i] - sth[i-1];
// for(int i=1;i<=scnt;++i)
// printf("%d : %d\n",i,sth[i]);
cout<<ans;
return 0;
}
B题:
发觉他一共才100个元素,而且重量一共就四种诶。
那么对于每个重量,枚举一下这个重量有几个,判一判能不能塞进去。
拿的时候同一个重量贪心就好。
#include <bits/stdc++.h>
using namespace std;
int n;
int sth[4][100];
int pos[4];
int w1;
int W;
long long ans = -123123123;
bool gwei(int a, int b, int c, int d) {
long long Twei = 1ll * w1 * a + 1ll * (w1 + 1) * b + 1ll * (w1 + 2) * c + 1ll * (w1 + 3) * d;
if(Twei <= W) {
long long nans = sth[0][a] + sth[1][b] + sth[2][c] + sth[3][d];
ans = max(ans, nans);
return true;
}
return false;
}
int main() {
scanf("%d%d", &n, &W);
int nw, nv;
scanf("%d%d", &nw, &nv);
w1 = nw, sth[0][++pos[0]] = -nv;
for(int i=2;i<=n;++i) {
scanf("%d%d", &nw, &nv);
int det = nw - w1;
sth[det][++pos[det]] = -nv;
}
for(int i=0;i<=3;++i)
sort(sth[i] + 1, sth[i] + pos[i] + 1);
for(int i=0;i<=3;++i) {
for(int j=1;j<=pos[i];++j) {
sth[i][j] = -sth[i][j];
sth[i][j] += sth[i][j-1];
}
}
for(int i=0;i<=pos[0];++i) {
for(int j=0;j<=pos[1];++j) {
for(int k=0;k<=pos[2];++k) {
for(int l=0;l<=pos[3];++l) {
if(!gwei(i, j, k, l))
break;
}
}
}
}
cout<<ans;
return 0;
}
C题:
开局读了个假题,以为是让我求最大。
“这不抽出头尾几个元素写个爆搜瞎判一发就好了?”
写完了发现样例过不去,手玩也不对。
重新读了个题,原来是最小值。
“这不把抽头尾改成抽中间,然后max改min就好了?”
然后发觉自己假的有点猛,隧弃疗,赛后膜了下题解。
定义
M
A
X
MAX
MAX和
M
I
N
MIN
MIN分别为全局max和全局min,分情况讨论。
假设
M
A
X
MAX
MAX和
M
I
N
MIN
MIN分别在红色和蓝色,那么显然,对于每两个球,把大的那个丢给
M
A
X
MAX
MAX,小的那个丢给
M
I
N
MIN
MIN,一定最优。
如果
M
A
X
MAX
MAX和
M
I
N
MIN
MIN在同一个颜色,也就是说,这个颜色的
M
−
m
M-m
M−m已经是定值了。
那么,我们把所有的二元组先内部排序,然后再外部排序,这样之后,我们依次做红蓝swap这个操作。在第一次做之后,我们会进入"
M
I
N
和
M
A
X
MIN和MAX
MIN和MAX在同一个颜色里面"的状态,之后按顺序swap,可以保证一定对于另一个pack拿到最小值。
来证明一下上面那个“保证”
分配好
M
A
X
MAX
MAX和
M
I
N
MIN
MIN之后,我们先无脑把所有的较小值扔进未确定的包里面。
然后按照较小值从小到大排个序,依次swap两个包里面的值。
对于MAX和MIN所在的那个包裹,无论如何加值,都不会造成影响。
对于另一个包裹,我们保证了他的Min是单调取遍每个值的。且我们做了最少的事情使得这个Min拿到这个值,也就是说,我们对于max的更改是满足贪心的。
然后用multiset做就好,注意.erase的时候,里面要套个.find,不然会挂。
#include <bits/stdc++.h>
using namespace std;
pair<int, int>sth[200010];
multiset<int>R, B;
int n;
long long gans() {
return 1ll * (*R.rbegin() - *R.begin()) * (*B.rbegin() - *B.begin());
}
int main() {
scanf("%d", &n);
for(int i=1;i<=n;++i) {
int ma, mi;
scanf("%d%d",&ma, &mi);
if(ma < mi)
swap(ma, mi);
sth[i] = make_pair(mi, ma);
R.insert(mi), B.insert(ma);
}
sort(sth+1, sth+n+1);
long long ans = 0x3f3f3f3f3f3f3f3f;
ans = min(ans, gans());
for(int i=1;i<=n;++i) {
R.erase(R.find(sth[i].first)), B.erase(B.find(sth[i].second));
R.insert(sth[i].second), B.insert(sth[i].first);
ans = min(ans, gans());
}
cout<<ans;
return 0;
}
F题:
首先可以想到一个很显然的
O
(
n
2
)
O(n^2)
O(n2)的DP算法:
dp[i][j]表示,当前完成了前i个指令,有一个棋子在x[i],另一个在j的最小花费。
考虑转移,在输入第
i
+
1
i+1
i+1个指令的时候,有两种决策:把位于x[i]的棋子挪过去 / 把位于j的棋子挪过去。
对于第一种决策,可以写作
d
p
[
i
+
1
]
[
j
]
=
d
p
[
i
]
[
j
]
+
d
i
s
t
(
x
[
i
]
,
x
[
i
+
1
]
)
dp[i+1][j] = dp[i][j] + dist(x[i], x[i+1])
dp[i+1][j]=dp[i][j]+dist(x[i],x[i+1])
对于第二种决策,可以写作
d
p
[
i
+
1
]
[
x
[
i
]
]
=
m
i
n
(
d
p
[
i
]
[
j
]
+
d
i
s
t
(
j
,
x
[
i
+
1
]
)
)
dp[i+1][x[i]] = min(dp[i][j] + dist(j, x[i+1]))
dp[i+1][x[i]]=min(dp[i][j]+dist(j,x[i+1]))
两种决策是取min关系。
发觉第一种我们可以通过维护一个全局Delta来实现,因为全局加的是一个固定值。
第二种决策我们需要查询带绝对值的min(因为有dist),不好操作,拆成两段。
上线段树,线段树中的val[1]和val[2]分别是
D
P
[
?
]
[
l
o
c
]
−
D
e
l
t
a
−
l
o
c
DP[?][loc]-Delta-loc
DP[?][loc]−Delta−loc和
D
P
[
?
]
[
l
o
c
]
−
D
e
l
t
a
+
l
o
c
DP[?][loc]-Delta+loc
DP[?][loc]−Delta+loc,改的时候注意推一推怎么加怎么减。
最后全局查个min就好。
写的时候注意线段树里面的那个mid要记得除二啊!!!这东西我调了30min。
#include <bits/stdc++.h>
using namespace std;
int n, q, A, B;
int x[200010];
namespace sgt {
#define ls(k) a[k].ch[0]
#define rs(k) a[k].ch[1]
struct node {
int ch[2], l, r;
long long val[3];
}; node a[500010];
int cnt = 1;
inline void update(int k) {
for(int i=1;i<=2;++i)
a[k].val[i] = min(a[ls(k)].val[i], a[rs(k)].val[i]);
}
inline void build(int k, int l, int r) {
a[k].l = l, a[k].r = r;
if(l == r) {
a[k].val[1] = 1e15;
a[k].val[2] = 1e15;
return;
}
int mid = l + r >> 1;
ls(k) = ++cnt, build(ls(k), l, mid);
rs(k) = ++cnt, build(rs(k), mid+1, r);
update(k);
}
inline void cmin(int k, int tar, long long val) {
if(a[k].l == a[k].r) {
a[k].val[1] = min(a[k].val[1], val - a[k].l);
a[k].val[2] = min(a[k].val[2], val + a[k].l);
return;
}
int mid = a[k].l + a[k].r >> 1;
if(tar <= mid)
cmin(ls(k), tar, val);
else
cmin(rs(k), tar, val);
update(k);
}
inline void change(int k, int tar, long long val) {
if(a[k].l == a[k].r) {
a[k].val[1] = val - a[k].l;
a[k].val[2] = val + a[k].l;
return;
}
int mid = a[k].l + a[k].r >> 1;
if(tar <= mid)
change(ls(k), tar, val);
else
change(rs(k), tar, val);
update(k);
}
long long query(int k,int l,int r,int val) {
if(a[k].l == l && a[k].r == r)
return a[k].val[val];
int mid = a[k].l + a[k].r >> 1;
if(r <= mid)
return query(ls(k), l, r, val);
else if(l > mid)
return query(rs(k), l, r, val);
else
return min(query(ls(k), l, mid, val), query(rs(k), mid+1, r, val));
}
long long gans(int k) {
if(a[k].l == a[k].r)
return a[k].val[1] + a[k].l;
return min(gans(ls(k)), gans(rs(k)));
}
} using namespace sgt;
long long suplus = 0;
int ab(int a) {
return a > 0 ? a : -a;
}
long long Q(int l,int r,int type) {
if(l > r || l < 0 || r > n)
return 40000000000000000;
return query(1, l, r, type);
}
int main() {
scanf("%d%d", &n, &q);
scanf("%d%d", &A, &B);
for(int i=1;i<=q;++i) {
scanf("%d",&x[i]);
}
x[0] = A;
build(1, 1, n);
change(1, B, 0);
// printf("Cmin : %d %lld\n", B, 0);
// printf("Q 1 1 1 = %lld\n",Q(1,1,1));
for(int i=1;i<=q;++i) {
// printf("Q 1 1 1 = %lld\n",Q(1,1,1));
//树里面放的是,dp[i] - suplus的值。
int det = ab(x[i-1] - x[i]);
long long lmin = Q(1, x[i], 1);
long long rmin = Q(x[i], n, 2);
long long actdp = min(x[i] + lmin, rmin - x[i]) + suplus;
// printf("lmin = %lld rmin = %lld actdp = %lld suplus = %lld det = %lld\n", lmin, rmin, actdp, suplus, det);
suplus += det;
// printf("Cmin : %d %lld\n", x[i-1], actdp - suplus);
cmin(1, x[i-1], actdp - suplus);
// printf("Q 1 1 1 = %lld\n",Q(1,1,1));
}
cout<<gans(1) + suplus;
return 0;
}
一场ARC下来感觉自己菜的不行,果然太久没打个人赛是会GG