杭电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 
#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=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>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=ans[j])
				temp[in]=tree[cur].v[i++];	
			else temp[in]=ans[j++];
		}	
		while(in=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
#include
#include
#include
#include
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;
}
 

 

你可能感兴趣的:(=====数据结构=====,线段树)