2021-08-16 SSL 模拟赛 T4 【斐波那契数列】【数学】【链表或并查集】

2021-08-16 SSL 模拟赛 T4

在这里插入图片描述
在这里插入图片描述
Quantask 大爷,stoorz 大爷,my_dog 大爷 yyds!!!!!

题目大意:

给你一堆集合,每次操作可以往 [ l , r ] [l,r] [l,r] 内的集合加入一个数,或者询问从 [ l , r ] [l,r] [l,r]的所有集合中是否存在三个数可以作为一个三角形的边长。

思路:

我们考虑一个集合内的数怎么样才能无法构成一个三角形。
假设 a [ 1 ] = 1 , a [ 2 ] = 2 a[1]=1,a[2]=2 a[1]=1,a[2]=2 那么 m i n ( a [ 3 ] ) = 2 min(a[3])=2 min(a[3])=2,继续推, m i n ( a [ 4 ] ) = 3 min(a[4])=3 min(a[4])=3
然后我们发现 m i n ( a [ i ] ) = a [ i − 1 ] + a [ i − 2 ] min(a[i])=a[i-1]+a[i-2] min(a[i])=a[i1]+a[i2],这不就是斐波那契数列吗?
然后看到题目中的 x < = 1 0 9 x<=10^9 x<=109,发现在斐波那契数列中第 45 项就已经超过了 1 0 9 10^9 109,所以如果一个集合内数的个数 > = 45 >=45 >=45 个,那么这个集合一定可以拼出三角形。
然后我们每次加数时如果遇到超过45 个数的集合就跳过,这个过程可以用一个链表或并查集来维护。
然后查询的时候,如果有集合内元素的个数超过 45 个就说明可以,如果没超过 45 个数的一群集合内的元素个数和超过 45 个也可以拼成。然后再把剩下的数排序去判断即可。
时间复杂度大约是 O ( ( n + m ) ∗ 45 ) O((n+m)*45) O((n+m)45),最好使用快读快输。
我写的是链表维护,并查集大家可以自行思考。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=1e5+10,lim=45;
ll n,m,a[V][lim+5],nxt[V],f[V],top[V],pre[V];
ll x,y,k,tot,b[V];
string s;
ll in()
{
	ll res=0,f=1;
	char ch;
	while((ch=getchar())<'0'||ch>'9')
	 if(ch=='-') f=-1;
	res=res*10+ch-48;
	while((ch=getchar())>='0'&&ch<='9')
	 res=res*10+ch-48;
	return res*f;
}
bool check()
{
	if(tot>=lim) return true;
	sort(b+1,b+tot+1);
	rep(i,3,tot)
	 if(b[i]<b[i-1]+b[i-2]) return true;
	return false;
}
int main()
{
	scanf("%lld%lld",&n,&m);
	rep(i,1,n)
	{
		a[i][1]=in();
		nxt[i]=i+1;pre[i]=i-1;
		++top[i];
	}
	rep(i,1,m)
	{
		cin>>s;
		if(s=="Quant")
		{
			x=in(),y=in(),k=in();
			for(r ll j=x;j<=y;j=nxt[j])
			{
				if(top[j]>=lim)
				{
					pre[nxt[j]]=pre[j];
					nxt[pre[j]]=nxt[j];
					continue ;
				}
				a[j][++top[j]]=k;
			}
		}
		else 
		{
			x=in(),y=in();
			tot=0;
			bool flag=false;
			for(r ll j=x;j<=y;j=nxt[j])
			{
				if(nxt[j]!=j+1||top[j]>=lim)
				{
					flag=true;
					printf("stoorz\n");
					break ;
				}
				rep(k,1,top[j]) b[++tot]=a[j][k];
                                if(tot>=lim)
				{
					flag=true;
					printf("stoorz\n");
					break ;
				}
			}
			if(!flag)
			{
				if(check()) printf("stoorz\n");
				else printf("my_dog\n");
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值