中国石油大学训练赛第九场

问题 C: 给你一个666

题目

题目描述

Tongtong非常喜欢用“say 666”的方式来打招呼,因此热爱数学的他找到了一个说666的新方式。Tongtong构造了一个数学上很6的运算。定义一个6位二进制数上的运算 @ : a@b=(c,d)。其中 c = a的高3位*b的低3位 ; d = a的低3位*b的高3位。例如 010 001 @ 011 001 = (010*001 , 001*011) = (2*1,1*3) = (2,3) 。
Tongtong给出了两个操作数a和b。以及一个数列 x1,x2,x3 ... xn ,假设a@b的结果(c,d),Tongtong非常关心数列在区间 [ min(c,d)*min(a,b) ,max(c,d)*max(a,b) ]上的最小值和最大值,Tongtong认为上述区间上的最大值和最小值可以代表666的程度,所以每组操作数都要计算出这两个最值。由于时间紧迫,他需要你来帮助他完成这个工作。

输入

第一行输入两个正整数 n,q,分别表示数列数字的个数和询问个数.其中1<=n<=50 000,1<=q<=100 000。
第二行输入n个非负整数,表示数列中的元素x1,x2 ... xn, 每个元素都在int类型的范围内。
接下来q行,每行给出一对非负整数,a,b,其意义见题面。本题保证所有的a和b均为6位无符号整数。

输出

对于每个询问,输出一对整数,分别表示目标区间上的最大值和最小值.每个询问的结果单独占一行。
请不要输出多余的空行。

样例输入

12 1
5 2 3 4 5 6 7 8 1 6 5 1
1 8

样例输出

8 2

提示

min(x,y)表示x和y的最小值, max(x,y)表示x和y的最大值.区间下标从1开始。
样例:
数列在区间[1,8]上的所有元素为{5 2 3 4 5 6 7 8},最大值为8,最小值为2。
若左边界越界则取1,若右边界越界则取n。

分析

线段树维护区间最大值和最小值,注意两边越界都要判断,即 l 可能大于n,r 也可能小于1。

代码

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f
typedef long long ll;
const int N=5e4+5;
int a,b,c,d;
struct node{
	int left,right;
	int num,mini,maxi;
}tr[N<<2];
void pushup(int m)
{
	tr[m].mini=min(tr[m<<1].mini,min(tr[m].mini,tr[m<<1|1].mini));
	tr[m].maxi=max(tr[m<<1].maxi,max(tr[m].maxi,tr[m<<1|1].maxi));
}
void build(int m,int l,int r)
{
	tr[m].left=l;
	tr[m].right=r;
	tr[m].mini=inf;
	tr[m].maxi=-inf;
	if(l==r)
	{
		scanf("%d",&tr[m].num);
		tr[m].mini=tr[m].num;
		tr[m].maxi=tr[m].num;
		return ;
	}
	int mid=(l+r)>>1;
	build(m<<1,l,mid);
	build(m<<1|1,mid+1,r);
	pushup(m);
}
int querymin(int m,int l,int r)
{
	if(tr[m].left==l&&tr[m].right==r)
		return tr[m].mini;
	int mid=(tr[m].left+tr[m].right)>>1;
	if(mid>=r) return querymin(m<<1,l,r);
	else if(mid<l) return querymin(m<<1|1,l,r);
	else return min(querymin(m<<1,l,mid),querymin(m<<1|1,mid+1,r));
}
int querymax(int m,int l,int r)
{
	if(tr[m].left==l&&tr[m].right==r)
		return tr[m].maxi;
	int mid=(tr[m].left+tr[m].right)>>1;
	if(mid>=r) return querymax(m<<1,l,r);
	else if(mid<l) return querymax(m<<1|1,l,r);
	else return max(querymax(m<<1,l,mid),querymax(m<<1|1,mid+1,r));
}
int getcd(int x,int y)
{
	int numa[8],numb[8];
	memset(numa,0,sizeof(numa));
	memset(numb,0,sizeof(numb));
	int i=6;
	while(x!=0)
	{
		numa[i]=x%2;
		x/=2;
		i--;
	}
	i=6;
	while(y!=0)
	{
		numb[i]=y%2;
		y/=2;
		i--;
	}
	int suma=numa[3]+2*numa[2]+4*numa[1];
	int sumb=numb[6]+2*numb[5]+4*numb[4];
	c=suma*sumb;
	suma=numa[6]+2*numa[5]+4*numa[4];
	sumb=numb[3]+2*numb[2]+4*numb[1];
	d=suma*sumb;
}
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	build(1,1,n);
	for(int i=0;i<q;i++)
	{
		scanf("%d%d",&a,&b);
		getcd(a,b);
		int l=min(a,b)*min(c,d);
		int r=max(a,b)*max(c,d);
		if(l<1||l>n) l=1;
		if(r>n||r<1) r=n;
	//	cout<<"l="<<l<<"r="<<r<<endl;
		printf("%d ",querymax(1,l,r));
		printf("%d\n",querymin(1,l,r));
	}
	return 0;
}

问题 D: LiMn2O4的数学之路

题目

减去的第n项。

分析

该公式的第n项就是斐波那契数列的第n项,然后用矩阵快速幂解决就ok了。

代码

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll mod=1e9+7;
struct mat{
	ll mp[2][2];
};
mat mul(mat A,mat B)
{
	mat ans;
	memset(ans.mp,0,sizeof(ans.mp));
	for(int i=0;i<2;i++)
	{
		for(int j=0;j<2;j++)
		{
			for(int k=0;k<2;k++)
				ans.mp[i][j]=(ans.mp[i][j]+A.mp[i][k]*B.mp[k][j])%mod;
		}
	}
	return ans;
}
mat quickmod(mat a,int b)
{
	mat ans;
	ans.mp[0][0]=1;
	ans.mp[0][1]=0;
	ans.mp[1][0]=0;
	ans.mp[1][1]=1;
	while(b)
	{
		if(b&1)
			ans=mul(ans,a);
		b>>=1;
		a=mul(a,a);
	}
	return ans;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		mat a;
		a.mp[0][0]=1;
		a.mp[0][1]=1;
		a.mp[1][0]=1;
		a.mp[1][1]=0;
		mat ans=quickmod(a,n-1);
		printf("%lld\n",ans.mp[0][0]);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值