首页 > 其他分享 >解题报告——灵活利用题目单调性省下复杂度

解题报告——灵活利用题目单调性省下复杂度

时间:2024-11-18 15:57:00浏览次数:1  
标签:q1 lfloor q2 int 复杂度 rfloor 解题 性省 size

有一种题目,需要直接/间接查询全局最值,并且带修改。

直接 set/priority_queue 不完了吗?

然而,这类题目通常具有巨大的操作量,朴素的需要额外复杂度来维护内部性质的数据结构(例如需要带一个 \(\log\))往往无法通过此类题目。

但是,这种题目本身一般具有某种单调性质,这使得我们可以使用一些复杂度更低的方法来维护此题的答案。

例题一:洛谷 P6033 [NOIP2004 提高组] 合并果子 加强版

一个小贪心:肯定是每次选最小的两个合并最优。

直接全插入堆中模拟不完了?哈夫曼树板子好吧!

数据范围:\(n\le 10^7,a_i\le 10^5\)。

复杂度 \(n \log n\) 直接爆炸。

显然是要线性做法。

发现 \(a_i\le 10^5\),桶排序可以做到 \(O(n)\)。

这样原序列就是单调不减的了。

性质:合并得越晚,合并所得到的值越大。

显然的吧,不证了。

注意到每次的最小值一定是没合并的最小值与合并后的最小值之一。

而合并后的值单调不降。

考虑建两个队列,一个队列存没有合并过的,另一个存合并得到的。

这样每次的最小值一定是两个队列的队首部分。

复杂度线性。

const int N=1e7+10,M=1e5+10;
int n,a[N],cnt[M];
ll ans;
deque<ll>q1,q2;
main() {
	n=read();
	FOR(i,1,n) a[i]=read(),++cnt[a[i]];
	FOR(i,0,M-1) while(cnt[i]) q1.push_back(i),--cnt[i];
	while(q1.size()||q2.size()) {
		ll k=1e18,x=1e18,y=1e18,z=1e18;
		if(q1.size()>1) x=q1[0]+q1[1];
		if(q1.size()&&q2.size()) y=q1[0]+q2[0];
		if(q2.size()>1) z=q2[0]+q2[1];
		if(!(q1.size()>1||(q1.size()&&q2.size())||q2.size()>1)) {
			cout<<ans<<"\n";
			return 0;
		}
		k=min(x,min(y,z));
		if(k==z) q2.pop_front(),q2.pop_front();
		else if(k==y) q1.pop_front(),q2.pop_front();
		else if(k==x) q1.pop_front(),q1.pop_front();
		q2.push_back(k);
		ans+=k;
	}
	return 0;
}

太简单了?

上强度!

例题二:[NOIP2016 提高组] 蚯蚓

先按照蚯蚓长度从小到大排序。

发现直接暴力 \(+q\) 肯定不可取。

考虑偏移量,每次取真实长度时加上 \(\Delta\)。

问题来了,怎么找最长的蚯蚓呢,查询 \(5\times 10^6\) 肯定不能用堆。

性质:每一种蚯蚓(没被切,左半部分,右半部分)长度单调不升。

证明:

首先没被切的肯定单调(排好序了)。

先考虑 \(q=0\)。

设当前最长为 \(x_0\),次长为 \(x_1\)。

先证左半部分。

\[∵ x_0\ge x_1\\ ∴ p x_0\ge p x_1\\ ∴ \lfloor p x_0\rfloor \ge \lfloor p x_1\rfloor. \]

再证右半部分。

\(x_1 \ge x_2 \land x_1, x_2 \in \Z\Rightarrow x_1 - x_2 \in \N\)。又 $ ∵ 0 <p < 1$,故有:

\[\begin{aligned}x_1 - x_2 &\ge p(x_1 - x_2) \\ x_1 - x_2 + p x_2 & \ge px_1 \\ \lfloor px_2 + (x_1 - x_2) \rfloor & \ge\lfloor px_1 \rfloor \\ \lfloor px_2\rfloor + (x_1 - x_2) & \ge \lfloor px_1 \rfloor \\ x_1 - \lfloor px_1 \rfloor & \ge x_2 - \lfloor px_2 \rfloor \end{aligned} \]

再考虑 \(q>0\)。

则后面切的为 \(x_1+q\)。

左半部分:

\[\lfloor p x_0\rfloor \ge \lfloor p x_1+pq \rfloor=\lfloor p(x_1+q) \rfloor \]

右半部分:

\[x_1 - \lfloor px_1\rfloor+ q \ge x_2 +q - \lfloor px_2\rfloor \ge x_2 + q - \lfloor p(x_2 +q) \rfloor \]

证毕。

综上所述,可以用 \(3\) 个队列分别维护 \(3\) 种蚯蚓,不断取出 \(3\) 个队头最大值。

时间复杂度 \(O(n\log n+m)\)。

const int N=1e5+10,M=7e6+10;
int n,m,q,u,v,t,k,in,a[N];
ll Delta;
queue<ll>A,B,C;
int cmp(int x,int y) {return x>y;}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m>>q>>u>>v>>t;
	for(int i=1;i<=n;++i) cin>>a[i];
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;++i) A.push(a[i]);
	for(int i=1;i<=m;++i) {
		ll x=-1e9;
		if(A.size()) x=A.front();
		if(B.size()) x=max(x,B.front());
		if(C.size()) x=max(x,C.front());
		if(A.size()&&A.front()==x) A.pop();
		else if(B.size()&&B.front()==x) B.pop();
		else if(C.size()&&C.front()==x) C.pop();
		x+=Delta;
		if(i%t==0) cout<<x<<" ";
		ll num1=x*u/v-q-Delta,num2=x-x*u/v-q-Delta;
		B.push(num1);
		C.push(num2);
		Delta+=q;
	}
	cout<<"\n";
	while(A.size()||B.size()||C.size()) {
		ll x=-1e9;
		if(A.size()) x=A.front();
		if(B.size()) x=max(x,B.front());
		if(C.size()) x=max(x,C.front());
		if(A.size()&&A.front()==x) A.pop();
		else if(B.size()&&B.front()==x) B.pop();
		else if(C.size()&&C.front()==x) C.pop();
		if((++in)%t==0) cout<<x+Delta<<" ";
	}
	cout<<"\n";
	return 0;
}

下面来一道 NOI/NOI+/CTSC 的题目!

例题三:[CSP-S2020] 贪吃蛇

个人感觉比例题二要简单一些。

思路证明

单调性:吃东西后的蛇越往后越弱。

证明:显然最强的蛇吃东西后越来越弱,最弱的蛇被吃掉后越来越强。

两者相减即得原命题。

证毕。

直接建两个队列维护没吃过的和吃过的蛇,直接模拟。

复杂度线性。

const int N=1e6+10;
int t,n,a[N],ans;
deque<pair<int,int> >q1,q2;
void Solve() {
	q1.clear(),q2.clear();
	FOR(i,1,n) q1.pb(mkp(a[i],i));
	while(1) {
		if(q1.size()+q2.size()==2) {
			ans=1;
			break;
		}
		auto z=q1.front();q1.pt();
		pair<int,int>x;
		if(!q2.size()||q1.size()&&q1.back()>q2.back()) x=q1.back(),q1.pk();
		else x=q2.back(),q2.pk();
		auto et=mkp(x.fr-z.fr,x.se);
		if(!q1.size()||q1.front()>et) {
			ans=q1.size()+q2.size()+2;
			int cnt=0;
			while(1) {
				++cnt;
				if(q1.size()+q2.size()+1==2) {
					ans-=(1-(cnt&1));
					break; 
				}
				pair<int,int>x;
				if(!q2.size()||q1.size()&&q1.back()>q2.back()) x=q1.back(),q1.pk();
				else x=q2.back(),q2.pk();
				et=mkp(x.fr-et.fr,x.se);
				if(!((!q1.size()||q1.front()>et)&&(!q2.size()||q2.front()>et))) {
					ans-=(1-(cnt&1));
					break; 
				} 
			} 
			break;
		} else q2.pf(et);
	} 
	cout<<ans<<"\n";
} 
main() {
	t=read()-1;
	cin>>n;
	FOR(i,1,n) a[i]=read();
	Solve();
	while(t--) {
		int k=read();
		FOR(i,1,k) {
			int x=read(),y=read();
			a[x]=y;
		}
		Solve();
	}
	return 0;
}

听说有拿 set 水过的。

标签:q1,lfloor,q2,int,复杂度,rfloor,解题,性省,size
From: https://www.cnblogs.com/zengziquan/p/18493500

相关文章

  • C++时间复杂度讲解
    它约等于算法中基本操作重复执行的次数(循环或递归的次数)不是行数!!!最多为O(5)!!!用乘号连接(在嵌套循环中),时间复杂度用O()表示。(O()只是符号)如:for(int=1;i<=n*10/8;i++){      for(intj=1;j<=n*10/2;k++){             for(intk=1;k<n*10;k++)......
  • CTF web解题 PHP http referer xff使用 burpsuite使用 新手入门 [SWPUCTF 2022 新生赛
    每日emo:burp可以抓包,你可以抓住到她的心吗?[SWPUCTF2022新生赛]xffFlag:NSSCTF{th1s_xff_1s_e4ay}打开靶机抓个包看一下根据打开靶机显示MustbeaccessedfromXiaohong'sowncomputer.传入X-Forwarded-For到127.0.0.1根据提示添加Referer到127.0.0.1......
  • 【11.16T1 公路】 --时间复杂度的计算技巧
    给定\(n\)个点\(m\)条边的无向简单连通图,每条边有颜色\(c_i\),当第\(k\)次经过颜色为\(j\)的边时,需要花费\(k\cdotx_j\)的代价。求在经过边数最小的情况下,\(1\)到各个点的最短路\(n\le50,m\le\binom{n}{2},x_i\le10^4\)做法是简单的,直接处理出最短路\(DAG\)......
  • [Codeforces Round 987 (Div. 2)](https://codeforces.com/contest/2031)解题报告
    CodeforcesRound987(Div.2)太好了是阳间场,我们有救了感觉脑子生锈了qwq,F题做不出来A分析知如果有\(i<j\)且\(a_i>a_j\)的情况出现则\(i\)和\(j\)一定至少改一个。所以答案即为\(n-cnt\),\(cnt\)为众数个数。B发现一个数离自己原本的位置距离不会超过\(1\),有......
  • 服务注册自治,降低 ASP.NET Core Web API 依赖注入的耦合度和复杂度
    前言在软件的实际开发中,一个软件通常由多个项目组成,这些项目都会直接或者间接被主ASP.NETCore项目引用。这些项目中通常都会用到若干个被注入的服务,因此我们需要在主ASP.NETCore项目的Program.cs中注册这些服务。这样不仅会增加了Program.cs管理的复杂度,而且也增加了......
  • 算法复杂度
    递归复杂度接前面常见初等函数的变化曲线T(n)=......
  • 洛谷解题日记||基础篇4
     #include<iostream>usingnamespacestd;intmain(){intn,m;cin>>n>>m;//计算所有矩形的数量longlongtotalRectangles=(longlong)(n*(n+1)/2)*(longlong)(m*(m+1)/2);//计算正方形的数量longlongt......
  • xss-labs-master靶机1-20关解题思路
    xss-labs-master靶机1-20关解题思路xss-labs-master靶机是xss-labs作者在github上发布的后来不知道为什么就把它删了,可能是因为这个靶机属于静态页面有一个通用的推广方式(具体在后面),不过还是希望读者使用实战中的攻击手段学习,这个靶机是对XSS漏洞的专项练习,一共有二十关,也是小......
  • 网络安全ctf比赛/学习资源整理,解题工具、比赛时间、解题思路、实战靶场推荐收藏!
    前言对于想学习或者参加CTF比赛的朋友来说,CTF工具、练习靶场必不可少,今天给大家分享自己收藏的CTF资源,希望能对各位有所帮助。CTF在线工具首先给大家推荐我自己常用的3个CTF在线工具网站,内容齐全,收藏备用。1、CTF在线工具箱:http://ctf.ssleye.com/包含CTF比赛中常用的编码......
  • The 3rd Universal Cup. Stage 15: Chengdu 解题集
    A.ArrowaRow我们把整个序列划分成若干个形似\(\text{>>>>..>>}\)的连续段,并尝试用一个最右边连通块里最左边的\(\text{>>>}\)去覆盖掉左边不和它在一个段里的所有\(\text{>}\),如果最右边的连续段长度小于2或者没有连续段则肯定无解。对于在同一个连续段里其他的\(\te......