[CF1513F]Swapping Problem

Swapping Problem

题解

很水的一道题

我们考虑交换那些数时会对答案产生贡献。
我们可以先将所有的线段分成 a > b a>b a>b a < b aa<b的两类,分别记作 x , y x,y x,y,很明显,同一类中的交换,绝对是不优的。
而不同的类中的交换,若 x , y x,y x,y没有重合部分,那么也是不优的。对于有重合部分的 x , y x,y x,y,它们所产生的贡献就是它们重合部分的两倍。这个手画一下就知道了
所以,我们要做的是,从两个集合中找出重合部分最长的两个线段。

我们可以先将所有的 y y y按左端点排序,再去掉所有被包含的线段,即排序后右端点不大于前者右端点的。
很明显,子线段是不会优于母线段的。
这样,我们就可以保证无论是左端点还是右端点,都是不降的了。
对于每个 x x x,我们先二分出与其左端点相交的最近的和与右端点相交的最近的线段,这两者明显对于所有没有包含于它的线段是最优的。
它们之间的线段是整段都被包含,它们的长度就是它们的贡献,我们只需要用st表找出它们的最大长度即可。

时间复杂度 O ( n l o g   n ) O\left(nlog\,n\right) O(nlogn)

源码

#include
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef unsigned int uint;
const int INF=0x7f7f7f7f;
const int jzm=233;
const int mo=998244353;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){
     return x<0?-x:x;}
template<typename _T>
void read(_T &x){
     
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){
     if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){
     x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,tot1,tot2,lg[MAXN],st[22][MAXN];LL num,ans;
struct ming{
     int a,b;}s[MAXN],up[MAXN],dn[MAXN];
bool cmp(ming x,ming y){
     return x.a<y.a;}
int Cover(ming x,ming y){
     int l=max(x.a,y.a),r=min(x.b,y.b);if(l>r)return 0;return r-l;}
int query(int l,int r){
     if(l>r)return 0;int len=r-l+1;return max(st[lg[len]][l],st[lg[len]][r-(1<<lg[len])+1]);}
int main(){
     
	read(n);
	for(int i=1;i<=n;i++)read(s[i].a);
	for(int i=1;i<=n;i++)read(s[i].b),ans+=1ll*Fabs(s[i].a-s[i].b);
	for(int i=1;i<=n;i++)(s[i].a<s[i].b?up[++tot1]:dn[++tot2])=s[i];
	for(int i=1;i<=tot2;i++)swap(dn[i].a,dn[i].b);
	sort(dn+1,dn+tot2+1,cmp);
	for(int i=1;i<=tot2;i++)dn[i].b=max(dn[i-1].b,dn[i].b);  
	for(int i=2;i<=tot2;i++)lg[i]=lg[i>>1]+1;
	for(int i=1;i<=tot2;i++)st[0][i]=dn[i].b-dn[i].a;
	for(int i=1;i<=20;i++)
		for(int j=1;j<=tot2-(1<<i)+1;j++)
			st[i][j]=max(st[i-1][j],st[i-1][j+(1<<i-1)]);
	for(int i=1;i<=tot1;i++){
     
		int al,ar,l=1,r=tot2;
		while(l<r){
     int mid=l+r+1>>1;if(dn[mid].a<=up[i].a)l=mid;else r=mid-1;}al=l;
		l=1,r=tot2;while(l<r){
     int mid=l+r>>1;if(dn[mid].b>=up[i].b)r=mid;else l=mid+1;}ar=l;
		num=max(num,1ll*max(max(Cover(up[i],dn[al]),Cover(up[i],dn[ar])),query(al+1,ar-1)));
		//printf("%d %d %d:%d %d %d %d\n",i,up[i].a,up[i].b,dn[al].a,dn[al].b,dn[ar].a,dn[ar].b);
	}
	printf("%lld\n",ans-2ll*num);
	return 0;
}

谢谢!!!

你可能感兴趣的:(------二分------)