[NOI2019]序列

题目链接
听大佬讲题,太神辣(
一道思维和细节兼顾的毒瘤题。

题目大意:我认为题意讲的很清楚。

64pts
直接思考正解是不太现实的,先从部分分入手。
观察到前64分与后36分有较大差距,可以从这里切入。
一般来说,对于匹配题基本都是这几个套路:DP、贪心、网络流、二分。
DP自然可以64pts,然而没法优化;
反悔贪心可做,然而思路没那么自然好想;
二分……据说可以做,但是不会/kk;
之后就是网络流。
对于题目,我们可以建立出如下的图:
在这里插入图片描述

然后跑最大费用最大流,64pts就到手了。

100pts
考试时64pts就可以跑了,不过这是练习,要争取拿到100pts。
考虑模拟一下费用流,分析情况。

若此时边 \((a_0,b_0)\) 仍有盈余流量,则此次增广肯定会选取左右两端未被选取的最大权值的点;
否则,分两种状况:

不退流
不退流的话,选取权值最大的一对 \((a_x,b_x)\) 就好了,增加费用为 \(a_x+b_x\)​ 。

退流
假设选择流 \((S->a_x->a_0->b_0->b_y->T'->T)\) 退流时有两种情况:选择两个点 \(a_z\)\(b_x\),或选择两个点 \(a_y\)、 $ b_z$;
这里以选择 \(a_z\)、$ b_x$ 为例:
选择了后,原来一个流变为两个流: \((S->a_x->b_x->T'->T)\)\((S->a_z->a_0->b_0->b_y->T'->T)\)
此时边 \((a_0,b_0)\) 流量仍为 \(K-L\),总费用增加了\(a_z+b_x\),找到可以使增加费用最大的流即可;
另一种情况同理,总费用增加了 \(a_y+b_z\)
将不退流和退流的两种情况结合起来,就可以得到本次增广增加的贡献。
开若干个堆维护,总共增广 \(K\) 次就能得到最终答案。

然而这道题除了足够的思维方面,实现细节也是多到离谱(毕竟NOI题)
为了维护数据,我们使用五个堆 \(a,b,a',b',ab\)

\(a,b\) 表示未匹配过的点;
\(a',b'\) 表示自身未匹配,但所对的点已经匹配过的点;
\(ab\) 表示一对均为匹配过的点。
然而把这些都维护完后,交上去WA一大片。因为还有一种特殊的状态:

从源点流出了两条流 \(a_x->b_y\)\(a_z->b_x\)
此时可以将两条流改为 \(a_x->b_x\)\(a_z->b_y\),这样就省下了 \((a0,b0)\) 的一点流量,可以做出更多的贡献。
具体的实现:设立一个调整函数,在退流和直接选取时检查是否出现特殊状况,如果有,就将 \((a_0,b_0)\) 的流量减去一。
到此此题就结束了_(:з」∠)_
总的来说作为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';
	}
}
posted @ 2022-08-09 20:18  Velix  阅读(41)  评论(0)    收藏  举报