杭电2019多校第二场 HDU-6601 Keen On Everything But Triangle(线段树+三角形与斐波那契数列 或主席树(模板))

链接:http://acm.hdu.edu.cn/showproblem.php?pid=6601、

题意:多组样例。给你一个n和q,接下来一行n个数,加下来q行,每行给出l、r,求区间[l,r]中的数,能组成三角形周长的最大值。不能组成则输出-1。

思路:首先,斐波那契数列中的任意三个数都不能组成三角形。如果若干数中,不能选出三个数组成三角形,那么他们肯定全是斐波那契数。因为斐波那契数列增长速度非常快,1e9范围内只有44个斐波那契数。又因为要找周长最长的三角形,所以我们用线段树维护区间内前50大的数即可,运气不好的话,有44个数全是斐波那契数,那么50个数中肯定能有组成三角形的数。询问时每次都找出区间内前50大的数,然后判断相邻的3个能不能组成三角形即可。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const int M = 50;
struct node
{
	int l,r,cnt,v[55];
}tree[N<<2];
int a[N],ans[55],temp[55],num,n,q;
void pushup(int cur)
{
	int i=1,j=1,cnt1=tree[cur<<1].cnt,cnt2=tree[cur<<1|1].cnt;
	while(tree[cur].cnt<M&&i<=cnt1&&j<=cnt2)
	{
		tree[cur].cnt++;
		if(tree[cur<<1].v[i]>=tree[cur<<1|1].v[j])
			tree[cur].v[tree[cur].cnt]=tree[cur<<1].v[i++];
		else
			tree[cur].v[tree[cur].cnt]=tree[cur<<1|1].v[j++];			
	}
	while(tree[cur].cnt<M&&i<=cnt1)
		tree[cur].v[++tree[cur].cnt]=tree[cur<<1].v[i++];
	while(tree[cur].cnt<M&&j<=cnt2)
		tree[cur].v[++tree[cur].cnt]=tree[cur<<1|1].v[j++];
	return ;
}
void build(int l,int r,int cur)
{
	tree[cur].l=l;
	tree[cur].r=r;
	tree[cur].cnt=0;
	if(l==r)
	{
		tree[cur].cnt=1;
		tree[cur].v[1]=a[l];
		return ;	
	}
	int m=(l+r)>>1;
	build(l,m,cur<<1);
	build(m+1,r,cur<<1|1);	
	pushup(cur);
	return ;
} 
void query(int l,int r,int cur)
{
	if(l<=tree[cur].l&&tree[cur].r<=r)
	{
		int i=1,j=1,in=0;
		while(in<M&&i<=tree[cur].cnt&&j<=num)
		{
			in++;
			if(tree[cur].v[i]>=ans[j])
				temp[in]=tree[cur].v[i++];	
			else temp[in]=ans[j++];
		}	
		while(in<M&&i<=tree[cur].cnt)
			temp[++in]=tree[cur].v[i++];
		while(in<M&&j<=num)
			temp[++in]=ans[j++];
		for(int i=1;i<=in;i++)
			ans[i]=temp[i];
		num=in;
		return ;
	}
	if(l<=tree[cur<<1].r) query(l,r,cur<<1);
	if(r>=tree[cur<<1|1].l) query(l,r,cur<<1|1);
	return ;
}
int main(void)
{
	int l,r;
	while(~scanf("%d%d",&n,&q))
	{
		num=0;
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		build(1,n,1);
		while(q--)
		{
			bool flag=1;
			scanf("%d%d",&l,&r);
			num=0; 
			query(l,r,1);
			if(num<3)
			{
				printf("-1\n");
				continue;
			}
			for(int i=1;i+2<=num;i++)
				if(ans[i+1]+ans[i+2]>ans[i])
				{
					printf("%lld\n",1LL*ans[i]+ans[i+1]+ans[i+2]);
					flag=0;
					break;	
				}
			if(flag) printf("-1\n");	
		}
	}
	return 0;
} 

主席树思路:问出区间前min(r-l+1,50)大的数即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define M(a, b) memset(a, b, sizeof(a))
#define lowbit(x) (x&(-x))
typedef long long ll;
const int N=1e5+10;
 
struct node
{
    int l,r;
    int val;
}tree[N * 22];
 
int n,q,a[N],id[N],root[N],cnt,ans[55],limit;
int build(int l,int r)
{
    int cur=cnt++;
    tree[cur].val=0;//cur的个数初始为0 
    if(l==r)
    {
    	//叶节点的区间左右端点为0
        tree[cur].l=0; 
        tree[cur].r=0;
        return cur;
    }
    int mid=(r+l)>>1;
    tree[cur].l=build(l,mid);//更新左端点 
    tree[cur].r=build(mid+1,r);//更新右端点 
    return cur;
}
int update(int up,int tar,int l,int r)
{
    int cur=cnt++;//增加新端点,为期编号 
    tree[cur]=tree[up];//先用上一个树的节点信息 
    tree[cur].val++;
    if(l==r) return cur;
    int mid=(r+l)>>1;
    //更新新建树的节点信息 
    if(tar<=mid) tree[cur].l=update(tree[up].l,tar,l,mid);
    else tree[cur].r=update(tree[up].r,tar,mid+1,r);
    return cur;
}
int query(int pl,int pr,int l,int r,int k)//询问区间第K大 
{
    if(l==r) return l;
    int mid=(r+l)>>1;
    //先看右子树数目是否够K个,够继续询问右子树 
    if(tree[tree[pr].r].val-tree[tree[pl].r].val>=k) return query(tree[pl].r,tree[pr].r,mid+1,r,k);
    //否则询问左子树第k-右子树个数大 
    else return query(tree[pl].l,tree[pr].l,l,mid,k-(tree[tree[pr].r].val-tree[tree[pl].r].val));
}
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            id[i]=a[i];
        }
        //离散化 
        sort(id+1,id+1+n);
        int len=unique(id+1,id+1+n)-(id+1);
        //初始化,只需要这一点,因为每次都用上一个树的信息 
        cnt=0;
        root[0]=build(1,len);
        
        for(int i=1;i<=n;i++)
        {
            int p=lower_bound(id+1,id+1+len,a[i])-id;
            root[i]=update(root[i-1],p,1,len);//插入新的点 
        }
        int l,r,k;
        while(q--)
        {
        	bool flag=1;
            scanf("%d%d",&l,&r);
            limit=min(r-l+1,50);
            for(int k=1;k<=limit;k++)
            	ans[k]=id[query(root[l-1],root[r],1,len,k)];
            for(int i=1;i+2<=limit;i++)
            {
            	if(ans[i+1]+ans[i+2]>ans[i])
            	{
            		printf("%lld\n",1LL*ans[i]+ans[i+1]+ans[i+2]);
            		flag=0;
            		break;
				}
			}
			if(flag) printf("-1\n");
        }
    }
 
	return 0;
}
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值