icpc2024昆明补题记录

D 套娃

这个trick是真没见过,也难怪场上没几个人过这个代码这么简单的题

题目大意

给定一排 \(n\) 个套娃,套娃的大小互不相同。你可以将相邻两个套娃套在一起,问最多能套几
次?

\[n ≤ 10^5 \]

题解

发现可以\(O(n)\)的判断一个长度为\(n\)的套娃序列是否能合并成一个,接下来从左边开始就可以贪心地往右扩展,尽量地让更多的套娃合并成一个,到合并不了的地方,说明只能合并这么多了
接下来就想到二分右端点,然后用\(O(n)\)的算法来进行二分的判断,但直接做复杂度是\(O(n^2\log n)\)
接下来比较精髓的trick就是,先用倍增确定二分的一个右端点,然后再二分,这样可以就可以让二分区间的长度去到\(O(n\log n)\),加上二分的复杂度,总复杂度就是\(O(n\log^2 n)\)

代码

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
void read(int &res)
{
	res=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=getchar(); 
}
const int N=1e5+100;
int T,n,a[N+10],p[N+10];
bool cmp(int x,int y)
{
	return a[x]<a[y];
}
struct node
{
	int l,r;
}sta[N+10];
int top;
bool check(int l,int r)
{
	for(int i=l;i<=r;i++)
		p[i]=i;
	sort(p+l,p+1+r,cmp);
	top=0;
	for(int i=l;i<=r;i++)
	{
		sta[++top]=(node){p[i],p[i]};
		while(top>1)
		{
			if(sta[top].l==sta[top-1].r+1)
			{
				sta[top-1].r=sta[top].r;
				top--;
				continue;
			}
			if(sta[top].r+1==sta[top-1].l)
			{
				sta[top-1].l=sta[top].l;
				top--;
				continue;
			}
			break;
		}
	}
	return top==1;
}
int main()
{
//	freopen("d.in","r",stdin);
	read(T);
	for(;T--;)
	{
		read(n);
		for(int i=1;i<=n;i++) read(a[i]);
		int Ans=0;
		for(int i=1;i<=n;i++)
		{
			int k=0;
			while(i+(1<<k)-1<=n&&check(i,i+(1<<k)-1)) k++;
			int l=i,r=min(n,i+(1<<k)-1),mid,ans=i;
			while(l<=r)
			{
				mid=l+r>>1;
				if(check(i,mid))
					ans=mid,l=mid+1;
				else
					r=mid-1;
			}
			Ans++;
			i=ans;
		}
		printf("%d\n",n-Ans);
	}
	return 0;
}
posted @ 2024-12-14 22:27  _doctorZ  阅读(57)  评论(0编辑  收藏  举报