题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5795
背景描述:
已经记不起来上一次做题是什么时候的事了。。。大概是上辈子?
要不是某只机房的脊蛙在群里毒我说我秒A。。。我才不做这题md
差点忘记说了。。。以后我一定要给我的ACM队起名叫:小脊蛙,呱尼玛!
解题报告:
首先题目中的序列称为序列
a
a
a和
b
b
b,规定定义序列
c
=
a
⋃
b
c = a\bigcup b
c=a⋃b
性质1:将
b
i
b_i
bi升序排列,若称对应的
a
i
a_i
ai和
b
j
b_j
bj为一对匹配
(
b
i
,
a
f
i
)
(b_i,a_{f_i})
(bi,afi),则最优解一定也满足
a
f
i
<
a
f
i
+
1
a_{f_i} < a_{f_{i + 1}}
afi<afi+1
证明:通过讨论
b
i
,
b
i
+
1
,
a
f
i
,
a
f
i
+
1
b_i,b_{i + 1},a_{f_i} ,a_{f_{i + 1}}
bi,bi+1,afi,afi+1的6种大小相对关系可以得出
性质2:将序列
c
c
c 升序排序,将其中的序列
b
b
b所有元素,以及选出来的序列
a
a
a元素全部标记,那么标记一定是一段一段的,那么对于每一段内部,含序列
a
a
a和序列
b
b
b元素个数相等,即各个段完全独立。
证明:这每一段的边界,要不是整个序列
c
c
c的边界,要不边界紧邻的非标记元素必然属于序列
a
a
a,如果一个属于序列
b
b
b元素匹配的不在本段中,则可以通过将匹配替换为边界紧邻的元素,而减小答案,因此每一个元素的匹配必在它所属的段内,因此段内属于
a
a
a和
b
b
b序列元素个数相等
有了这两个性质,做起来就简单了,每个段内的 a a a和 b b b元素相等,定义 a a a为-1, b b b为+1,求一个前缀和,那么一个段就是前缀和相同的两个位置之间的,但注意到我们只需要找到前缀和相同的离当前最近的那个位置,且这一段的答案很好算,因为这一段内必然是总满足 b i < a f i bi < a_{f_i} bi<afi或者 b i > a f i bi > a_{f_i} bi>afi,再做一个前缀和就好了,再根据以上 d p dp dp即可。。。
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define N 100000
#define N1 (N + 2)
#define inf 1e15
//#define ABS(x) ((x > 0) ? x : (-x))
using namespace std;
typedef long long LL;
struct node
{
LL val;
bool ID;
};
node c[N1 << 1];
int l,n,m,last[N1 << 1],S1[N1 << 1];
LL S2[N1 << 1],f[N1 << 1],a[N1],b[N1];
inline bool cmp(node x,node y)
{
return (x.val < y.val);
};
inline LL ABS(LL t)
{
return (t > 0) ? t : (-t);
}
void init()
{
scanf("%d%d",&n,&m);
l = n + m;
for (int i = 1;i <= n; ++i)
scanf("%lld",&a[i]);
for (int i = 1;i <= m; ++i)
scanf("%lld",&b[i]);
for (int i = 1;i <= n; ++i)
c[i].val = a[i],c[i].ID = 0;
for (int i = 1;i <= m; ++i)
c[i + n].val = b[i],c[i + n].ID = 1;
sort(c + 1,c + l + 1,cmp);
//for (int i = 1;i <= l; ++i)
//cout<<c[i].val<<"("<<c[i].ID<<") ";
//puts("");
}
void DO_IT()
{
S1[0] = m; S2[0] = 0; f[0] = 0;
for (int i = 0;i <= l; ++i) last[i] = -1;
last[m] = 0;
for (int i = 1;i <= l; ++i)
{
if (c[i].ID)
{
S1[i] = S1[i - 1] - 1;
S2[i] = S2[i - 1] - c[i].val;
int o = last[S1[i]];
if (o == -1||f[o] == inf) f[i] = inf;
else f[i] = f[o] + ABS(S2[i] - S2[o]);
}
else
{
S1[i] = S1[i - 1] + 1;
S2[i] = S2[i - 1] + c[i].val;
f[i] = f[i - 1];
int o = last[S1[i]];
if (f[o] == inf) f[i] = inf;
else f[i] = min(f[i],f[o] + ABS(S2[i] - S2[o]));
}
last[S1[i]] = i;
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
init();
DO_IT();
printf("%lld\n",f[l]);
}
}
总结:这道题怎么做其实并不重要,我只是非常想知道,脊蛙什么时候能找到妹子啊。。。