题目链接
听大佬讲题,太神辣(
一道思维和细节兼顾的毒瘤题。
题目大意:我认为题意讲的很清楚。
64pts
直接思考正解是不太现实的,先从部分分入手。
观察到前64分与后36分有较大差距,可以从这里切入。
一般来说,对于匹配题基本都是这几个套路:DP、贪心、网络流、二分。
DP自然可以64pts,然而没法优化;
反悔贪心可做,然而思路没那么自然好想;
二分……据说可以做,但是不会/kk;
之后就是网络流。
对于题目,我们可以建立出如下的图:
然后跑最大费用最大流,64pts就到手了。
100pts
考试时64pts就可以跑了,不过这是练习,要争取拿到100pts。
考虑模拟一下费用流,分析情况。
- 若此时边 ( a 0 , b 0 ) (a_0,b_0) (a0,b0) 仍有盈余流量,则此次增广肯定会选取左右两端未被选取的最大权值的点;
否则,分两种状况:
- 不退流
不退流的话,选取权值最大的一对 ( a x , b x ) (a_x,b_x) (ax,bx) 就好了,增加费用为 a x + b x a_x+b_x ax+bx 。
- 退流
假设选择流
(
S
−
>
a
x
−
>
a
0
−
>
b
0
−
>
b
y
−
>
T
′
−
>
T
)
(S->a_x->a_0->b_0->b_y->T'->T)
(S−>ax−>a0−>b0−>by−>T′−>T) 退流时有两种情况:选择两个点
a
z
a_z
az、
b
x
b_x
bx,或选择两个点
a
y
a_y
ay、
b
z
b_z
bz;
这里以选择
a
z
a_z
az、
b
x
b_x
bx 为例:
选择了后,原来一个流变为两个流:
(
S
−
>
a
x
−
>
b
x
−
>
T
′
−
>
T
)
(S->a_x->b_x->T'->T)
(S−>ax−>bx−>T′−>T),
(
S
−
>
a
z
−
>
a
0
−
>
b
0
−
>
b
y
−
>
T
′
−
>
T
)
(S->a_z->a_0->b_0->b_y->T'->T)
(S−>az−>a0−>b0−>by−>T′−>T)
此时边
(
a
0
,
b
0
)
(a_0,b_0)
(a0,b0) 流量仍为
K
−
L
K-L
K−L,总费用增加了
a
z
+
b
x
a_z+b_x
az+bx,找到可以使增加费用最大的流即可;
另一种情况同理,总费用增加了
a
y
+
b
z
a_y+b_z
ay+bz。
将不退流和退流的两种情况结合起来,就可以得到本次增广增加的贡献。
开若干个堆维护,总共增广
K
K
K 次就能得到最终答案。
然而这道题除了足够的思维方面,实现细节也是多到离谱(毕竟NOI题)
为了维护数据,我们使用五个堆
a
,
b
,
a
′
,
b
′
,
a
b
a,b,a',b',ab
a,b,a′,b′,ab
- a , b a,b a,b 表示未匹配过的点;
- a ′ , b ′ a',b' a′,b′ 表示自身未匹配,但所对的点已经匹配过的点;
- a b ab ab 表示一对均为匹配过的点。
然而把这些都维护完后,交上去WA一大片。因为还有一种特殊的状态:
- 从源点流出了两条流 a x − > b y a_x->b_y ax−>by, a z − > b x a_z->b_x az−>bx。
- 此时可以将两条流改为 a x − > b x a_x->b_x ax−>bx, a z − > b y a_z->b_y az−>by,这样就省下了 ( a 0 , b 0 ) (a0,b0) (a0,b0) 的一点流量,可以做出更多的贡献。
具体的实现:设立一个调整函数,在退流和直接选取时检查是否出现特殊状况,如果有,就将
(
a
0
,
b
0
)
(a_0,b_0)
(a0,b0) 的流量减去一。
到此此题就结束了_(:з」∠)_
总的来说作为NOI的D1T3,还是偏向简单的。
Code
#include<bits/stdc++.h>
#define N 200010
using namespace std;
priority_queue<pair<long long,int>>a0,a1,b0,b1,ab;//a0:未匹配的a,a1:b已匹配的a,ab:均未匹配的一对
int a[N],b[N],mat[2][N],used[N],t,n,k,l,cnt,flag,pos1,pos2;
long long ans,mx;
void del(priority_queue<pair<long long,int>> &x,int id)//id(0~4):a0,a1,b0,b1,ab
{
if(x.empty())return;
if(id<4)
while(mat[id>1][x.top().second]>0)
{
x.pop();
if(x.empty())return;
}
else while(used[x.top().second])
{
x.pop();
if(x.empty())return;
}
}
void adjust(int x)//松弛
{
if(mat[0][x]&&mat[1][x]&&mat[0][x]!=x&&mat[1][x]!=x)
{
mat[0][mat[1][x]]=mat[0][x];
mat[1][mat[0][x]]=mat[1][x];
if(mat[0][x]==mat[1][x])cnt++;
mat[0][x]=mat[1][x]=x;
cnt++;
}
}
void match0()//直接取两个未被匹配的
{
del(a0,0);del(b0,2);
long long u=a0.top().first,v=b0.top().first;
int x=a0.top().second,y=b0.top().second;
mat[0][x]=y;
mat[1][y]=x;
if(x!=y)cnt--;
used[x]=used[y]=1;
adjust(x);adjust(y);
if(!mat[1][x])b1.push(make_pair(b[x],x));
if(!mat[0][y])a1.push(make_pair(a[y],y));
ans+=u+v;
}
void match1()//取一对出来匹配
{
del(ab,4);
if(ab.empty())return;
long long u=ab.top().first;
int x=ab.top().second;
if(u>mx)
{
flag=1;
mx=u;
pos1=pos2=x;
}
}
void adjust1()
{
mat[0][pos1]=mat[1][pos2]=pos1;
ab.pop();
used[pos1]=1;
}
void match2()//取a1,b0出来匹配
{
del(a1,1);del(b0,2);
if(a1.empty()||b0.empty())return;
long long u=a1.top().first,v=b0.top().first;
int x=a1.top().second,y=b0.top().second;
if(u+v>mx)
{
flag=2;
mx=u+v;
pos1=x;
pos2=y;
}
}
void adjust2()
{
if(mat[1][pos1]==pos2)cnt++;
mat[0][mat[1][pos1]]=pos2;
mat[1][pos2]=mat[1][pos1];
mat[0][pos1]=mat[1][pos1]=pos1;
used[pos1]=1;used[pos2]=1;
a1.pop();b0.pop();
a1.push(make_pair(a[pos2],pos2));
adjust(pos2);
}
void match3()//取a0,b1出来匹配
{
del(a0,0);del(b1,3);
if(a0.empty()||b1.empty())return;
long long u=a0.top().first,v=b1.top().first;
int x=a0.top().second,y=b1.top().second;
if(u+v>mx)
{
flag=3;
mx=u+v;
pos1=x;
pos2=y;
}
}
void adjust3()
{
if(mat[0][pos2]==pos1)cnt++;
mat[1][mat[0][pos2]]=pos1;
mat[0][pos1]=mat[0][pos2];
mat[0][pos2]=mat[1][pos2]=pos2;
used[pos2]=1;used[pos1]=1;
a0.pop();b1.pop();
b1.push(make_pair(b[pos1],pos1));
adjust(pos1);
}
void init()
{
while(!a0.empty())a0.pop();
while(!a1.empty())a1.pop();
while(!b0.empty())b0.pop();
while(!b1.empty())b1.pop();
while(!ab.empty())ab.pop();
memset(mat,0,sizeof mat);
memset(used,0,sizeof used);
ans=0;
}
int main()
{
cin>>t;
while(t--)
{
init();
scanf("%d%d%d",&n,&k,&l);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1;i<=n;i++)
{
a0.push(make_pair(a[i],i));
b0.push(make_pair(b[i],i));
ab.push(make_pair(a[i]+b[i],i));
}
cnt=k-l;
while(k--)
{
if(cnt)
{
match0();
continue;
}
flag=mx=0;
match1();
match2();
match3();
switch(flag)
{
case 1:adjust1();break;
case 2:adjust2();break;
case 3:adjust3();break;
}
ans+=mx;
}
cout<<ans<<'\n';
}
}