首页 > 其他分享 >[CCO 2023] Line Town

[CCO 2023] Line Town

时间:2023-07-03 20:03:24浏览次数:42  
标签:Town cur seq int ll CCO qry Line 逆序

场上有点降智……其实会了互不相同的 sub 就可以会第一个 sub 甚至正解的。

题意

给定一个序列 \(a_i\),\(|a_i|\leq 10^9\),每次可以选两个相邻元素 \(\texttt{swap}\),然后将两个元素同时取反。问最少操作几次可以使数列不降。

做法

场上做法考虑 \(|a_i|\) 互不相同的部分分。我们考虑枚举最终有几个负数几个正数,零随便算它跟正数还是跟负数一块。那么负数绝对值降序,正数绝对值升序。我们再考虑将原序列复合上一个什么样的置换 \(p\) 才可以与最终序列的绝对值对应上。我们发现一个数最后有没有被取反只跟这个 \(p\) 有关。具体地,一个数 \(a_i\) 如果在最终的序列是 \(-a_i\),当且仅当在 \(p\) 中包含 \(i\) 的逆序对数是奇数个。

包含 \(i\) 的逆序对数的奇偶性我们在 Jewel of Data Structure Problems 这道题中见过。结论就是它的值等于 \(i\oplus p_i\) 的奇偶性。

那么我们首先对于所有奇数位置的 \(a_i\) 取个反,然后只用考虑 \(p_i\) 的贡献了。

如果我们将新得到的 \(a_i\) 负数标成 \(1\),正数标成 \(0\),我们相当于按绝对值从小到大填数,从中间往两边填入一些 \(01\),使得最终串变成 \(\color{blue}{\dots 01010}\color{red}{01010 \dots}\) 或者 \(\color{blue}{\dots 10101}\color{red}{10101 \dots}\) 这种形式。我们需要最小化的是 \(p\) 的逆序对数。我们发现每加一个数逆序对的变化只跟加的那个数和加在左边还是右边有关。记录一些奇偶性 DP 即可。

#include <cstdio>
#include <algorithm>
using namespace std;
int read(){/* read */}
typedef long long ll;
const int N=1000003;
const ll INF=0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> pii;
int n,a[N];
pii b[N];
ll f[2][2][2],g[2][2][2];
void chmn(ll &x,ll v){if(x>v) x=v;}
int tr[N];
int qry(int x){
	int res=0;
	for(int i=x;i;i^=(i&-i)) res+=tr[i];
	return res;
}
void upd(int x){
	for(int i=x;i<=n;i+=(i&-i)) ++tr[i];
}
int main(){
	n=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=1;i<=n;++i) if(i&1) a[i]=-a[i];
	for(int i=1;i<=n;++i) b[i]=pii(abs(a[i]),i);
	sort(b+1,b+n+1);
	for(int t=0;t<2;++t)
		for(int x=0;x<2;++x)
			for(int y=0;y<2;++y) f[t][x][y]=INF;
	f[1][0][0]=f[0][1][1]=0;
	for(int i=1;i<=n;++i){
		int p=b[i].second;
		int pre=qry(p),suf=i-1-qry(p);
		upd(p);
		bool c=(a[p]<0);
		for(int t=0;t<2;++t)
			for(int x=0;x<2;++x)
				for(int y=0;y<2;++y)
					g[t][x][y]=f[t][x][y],f[t][x][y]=INF;
		for(int t=0;t<2;++t)
			for(int x=0;x<2;++x)
				for(int y=0;y<2;++y){
					if(g[t][x][y]==INF) continue;
					if(c==x) chmn(f[t^1][x^1][y],g[t][x][y]+pre);
					if(c==y) chmn(f[t][x][y^1],g[t][x][y]+suf);
				}
	}
	ll res=INF;
	for(int x=0;x<2;++x)
		for(int y=0;y<2;++y) chmn(res,f[0][x][y]);
	if(res==INF) puts("-1");
	else printf("%lld\n",res);
	return 0;
}

考虑有重复数时怎么做。我们每次将一整段绝对值相同的劈成两个部分,分别加在序列的左边和右边。注意到对于序列的 \(01\) 情况有些限制,我们将绝对值相同且 01 情况相同的数拿下来排序。对于限制一定是 \(0\) 的位一定是按原数列下标从左往右填入所有的 \(0\),\(1\) 同理,所以我们只需要考虑新加入的段中 \(01\) 之间的逆序对,和新段与原序列形成的逆序对。

我们枚举如何划分新段,那么 \(01\) 情况的限制要么不变,要么每次只交换一对 \(01\),所以逆序对容易动态维护。新段与原序列的逆序对也可以用树状数组简单维护。剩下的 DP 部分和 \(|a_i|\) 互不相同的做法一样。

时间复杂度 \(O(n\log n)\)。看代码感觉自己的做法比其它人要麻烦点。

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int read(){/* read */}
typedef long long ll;
const int N=1000003;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n,a[N];
ll f[2][2][2],g[2][2][2];
void chmn(ll &x,ll v){if(x>v) x=v;}
struct bit{
	int tr[N],stk[N],tp;
	int qry(int x,bool t){
		int res=0;
		for(int i=x;i;i^=(i&-i)) res+=tr[i];
		if(t) return tp-res;
		return res;
	}
	void upd(int x){
		stk[++tp]=x;
		for(int i=x;i<=n;i+=(i&-i)) ++tr[i];
	}
	void clear(){
		while(tp){
			for(int i=stk[tp--];i<=n;i+=(i&-i))
				if(tr[i]) tr[i]=0;
				else break;
		}
	}
}F,G;
int buc[N],rk;
vector<int> v[N][2];
int seq[N];
int main(){
	n=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=1;i<=n;++i) if(i&1) a[i]=-a[i];
	for(int i=1;i<=n;++i) buc[++rk]=abs(a[i]);
	sort(buc+1,buc+rk+1);rk=unique(buc+1,buc+rk+1)-buc-1;
	for(int i=1;i<=n;++i)
		if(a[i]<0) v[lower_bound(buc+1,buc+rk+1,-a[i])-buc][1].emplace_back(i);
		else v[lower_bound(buc+1,buc+rk+1,a[i])-buc][0].emplace_back(i);
	for(int t=0;t<2;++t)
		for(int x=0;x<2;++x)
			for(int y=0;y<2;++y) f[t][x][y]=INF;
	if(buc[1]) f[1][0][0]=f[0][1][1]=0;
	else{
		int len=v[1][0].size();
		for(int x:v[1][0]) F.upd(x);
		for(int i=0;i<=len;++i){
			f[~i&1][i&1][(len^i)&1]=0;
			f[i&1][~i&1][~(len^i)&1]=0;
		}
	}
	for(int pos=1;pos<=rk;++pos){
		if(!buc[pos]) continue;
		int len=v[pos][0].size()+v[pos][1].size();
		for(int t=0;t<2;++t)
			for(int x=0;x<2;++x)
				for(int y=0;y<2;++y)
					g[t][x][y]=f[t][x][y],f[t][x][y]=INF;
		for(int t=0;t<2;++t)
			for(int x=0;x<2;++x)
				for(int y=0;y<2;++y){
					if(g[t][x][y]==INF) continue;
					if(x!=y){
						for(int tt=0;tt<2;++tt){
							int o[2]={0,0};
							ll cur=0;
							if(tt){
								if(v[pos][x].empty()) goto nxt;
								seq[0]=v[pos][x][o[x]++];
								cur+=F.qry(seq[0],0);
								G.upd(seq[0]);
							}
							for(int i=tt,par=y;i<len;++i,par^=1){
								if(o[par]>=int(v[pos][par].size())) goto nxt;
								seq[i]=v[pos][par][o[par]++];
								cur+=F.qry(seq[i],1)+G.qry(seq[i],1);
								G.upd(seq[i]);
							}
							if(o[0]<int(v[pos][0].size())||o[1]<int(v[pos][1].size())) goto nxt;
							chmn(f[t^tt][x^tt][y^(len&1)^tt],g[t][x][y]+cur);
							for(int i=tt;i+1<len;i+=2){
								cur-=F.qry(seq[i],1);
								cur-=F.qry(seq[i+1],1);
								cur+=F.qry(seq[i],0);
								cur+=F.qry(seq[i+1],0);
								chmn(f[t^tt][x^tt][y^(len&1)^tt],g[t][x][y]+cur);
							}
							nxt: G.clear();
						}
					}
					else{
						for(int tt=0;tt<2;++tt){
							int o[2]={0,0};
							ll cur=0;
							if(tt){
								if(v[pos][x].empty()) goto jump;
								seq[0]=v[pos][x][o[x]++];
								cur+=F.qry(seq[0],0);
								G.upd(seq[0]);
							}
							for(int i=tt,par=y;i<len;++i,par^=1){
								if(o[par]>=int(v[pos][par].size())) goto jump;
								seq[i]=v[pos][par][o[par]++];
								cur+=F.qry(seq[i],1)+G.qry(seq[i],1);
								G.upd(seq[i]);
							}
							if(o[0]<int(v[pos][0].size())||o[1]<int(v[pos][1].size())) goto jump;
							chmn(f[t^tt][x^tt][y^(len&1)^tt],g[t][x][y]+cur);
							for(int i=tt;i+1<len;i+=2){
								if(seq[i]>seq[i+1]) --cur;else ++cur;
								swap(seq[i],seq[i+1]);
								cur-=F.qry(seq[i],1);
								cur-=F.qry(seq[i+1],1);
								cur+=F.qry(seq[i],0);
								cur+=F.qry(seq[i+1],0);
								chmn(f[t^tt][x^tt][y^(len&1)^tt],g[t][x][y]+cur);
							}
							jump: G.clear();
						}
					}
				}
		for(int x:v[pos][0]) F.upd(x);
		for(int x:v[pos][1]) F.upd(x);
	}
	ll res=INF;
	for(int x=0;x<2;++x)
		for(int y=0;y<2;++y) chmn(res,f[0][x][y]);
	if(res==INF) puts("-1");
	else printf("%lld\n",res);
	return 0;
}

标签:Town,cur,seq,int,ll,CCO,qry,Line,逆序
From: https://www.cnblogs.com/yyyyxh/p/CCO2023_line_town.html

相关文章

  • Twisted @defer.inlineCallbacks
    @defer.inlineCallbacks是Twisted框架中的一个装饰器,用于定义基于协程的异步函数。在使用Twisted进行异步编程时,常见的方式是使用回调函数来处理异步操作的结果。但是使用回调函数可能会导致代码复杂、难以维护和阅读。因此,Twisted提供了@defer.inlineCallbacks装饰器,通......
  • draw line on image
    cv2.line(image,start_point,end_point,color,thickness)#Pythonprogramtoexplaincv2.line()method#importingcv2importcv2image=cv2.imread(path)start_point=(0,0)end_point=(250,250)color=(0,255,0)thickness=9#Usingcv2.line(......
  • IOS开发-设置UILabel行间距lineSpacing
    1.如何设置UILabel行间距lineSpacing UILabel是没有这么一个直接暴露的属性的,想要修改lineSpacing,我们需要借助NSAttributedString来实现。NSMutableParagraphStyle*style=[NSMutableParagraphStylenew];style.lineSpacing=15;NSMutableDictionary*attribu......
  • C++中三个特殊的宏 __FILE__, __FUNCTION__ 和 __LINE__
    有一次在看代码时,发现如下代码:m_strClassFileName=__FILE__;  把 __FILE__赋给了一个变量.这是我第一次接触__FILE__,于是查找了一下,才发现它是C++中三个特殊的宏之一.C++中共有三个特殊的宏,分别是 __FILE__,__FUNCTION__和__LINE__......
  • 说说设计模式~管道模式(pipeline)
    说明复合的责任链,类似于管道模式,只要符合条件,说会向下传递,不会终止算法说明按最高优先级去使用,符合就用,不符合就走下一个策略具体链条,有点像pipeline管道模式BlackHandlerip=172.17.0.11RateLimitHandlerheader=is-blackWriteBlackHandlerheader=real-black继承......
  • SAP FI - General Ledger&COA Group& Retained Earnings Account
    YoucancreateaSAPFIchartofaccountsgroupasperyourrequirement.ToeffectivelymanageandcontrolalargenumberofG/Laccounts,youshoulduseCOAgroups.HowtodefineChartofAccountsGroup?TherearetwowaysyoucancreateanewCOAgroup.T......
  • 备库归档日志文件的删除测试——第二篇:valid_for参数为ONLINE_LOGFILES与STANDBY_LOGF
    文档课题:备库归档日志文件的删除测试——第二篇:valid_for参数为ONLINE_LOGFILES与STANDBY_LOGFILES.数据库:oracle11.2.0.4架构:rac(2节点)+dg(orcldg与sh_orcl)场景描述:在该架构中,orcldg备库作为sh_orcl备库归档日志文件的来源,现测试以下两点:a、归档日志文件从orcldg......
  • mysql: [Warning] Using a password on the command line interface can be insecure.
      https://zhuanlan.zhihu.com/p/542166965 ......
  • Cake Assembly Line
    CakeAssemblyLinetimelimitpertest1secondmemorylimitpertest256megabytesinputstandardinputoutputstandardoutputAcakeassemblylineinabakerywasonceagainoptimized,andnow n cakesaremadeatatime!Inthelaststep,eachof......
  • Online Temporal Calibration for Monocular Visual-Inertial Systems
    摘要:准确的状态估计是各种智能应用的基本模块,例如机器人导航、自动驾驶、虚拟和增强现实。近年来,视觉和惯性融合是一种流行的技术,用于6自由度状态估计。不同传感器测量记录的时间点对于系统的鲁棒性和准确性非常重要。实际上,每个传感器的时间戳通常会受到触发和传输延迟的影响,导......