2017.11.6 T1 2037
题目描述
大数学家高斯小时候偶然间发现一种有趣的自然数集合 Blash ,对应以 a 为基的集合 Ba 定义如下:
(1)a 是集合 Ba 的基,且 a 是 Ba 的第一个元素。
(2)如果 x 在集合 Ba 中,则 2x+1 和 3x+1 也都在集合 Ba 中。
(3)没有其他元素在集合 Ba 中了。
现在小高斯想知道如果将集合 Ba 中元素按照升序排列,第 n 个元素会是多少?
输入格式
输入包含很多行,每行输入包括两个数字,集合的基 a 以及所求元素序号 n 。
输出格式
对应每个输入,输出集合 Ba 的第 n 个元素值。
样例数据
输入
1 100
28 5437
输出
418
900585
备注
【数据规模与约定】
对于 30% 的数据:1≤n≤1000;1≤a≤20;数据组数不超过5。
对于 70% 的数据:1≤n≤10000;1≤a≤30;数据组数不超过5。
对于 100% 的数据:1≤n≤1000000;1≤a≤50;数据组数不超过10。
分析:刚开始天真的用了优先对列,复杂度O( NlogN )感觉 106 级别能过的样子,中途样例第一组数据还老是算出来375(如果你也是,说明你忘了“集合”这个东西有特异性,没有重复元素),最后发现是10组数据orz。然后就只能想其他方法,最后还是队列,只是用单调队列,因为对于从小到大每个x的 2∗x+1 和 3∗x+1 分别也是从小到大的,所以就开两个队列分别存这两种,每次比较队首哪个更小就用它算 2∗x+1 和 3∗x+1 ,又放到两个队尾,再注意判重,最后就实现了O( N )查询了。
是不是想到了NOIP2016Day2T2蚯蚓呀?
代码
70%:被我扔掉的优先队列在哭泣……
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
int a,n,cnt;
long long las;
int main()
{
freopen("blash.in","r",stdin);
freopen("blash.out","w",stdout);
while(scanf("%d%d",&a,&n)!=EOF)
{
priority_queue<long long> que;
que.push(-(2*a+1)),que.push(-(3*a+1));
cnt=1,las=a;long long b;
while(cnt1)
{
cnt++;
b=-que.top();//判重
que.pop();
while(b==las)
{
b=-que.top();
que.pop();
}
que.push(-(2*b+1)),que.push(-(3*b+1));//手动小根堆
las=b;//记录队首,判重
}
b=-que.top();//出第n个的时候也要记得判重
while(b==las)
{
b=-que.top();
que.pop();
}
cout<'\n';
}
return 0;
}
100%:单调队列在嘲讽优先队列
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
const int maxn=1000010;
int a,n,head1,head2,tail1,tail2,cnt;
long long x,las,q1[maxn*3],q2[maxn*3];
int main()
{
freopen("blash.in","r",stdin);
freopen("blash.out","w",stdout);
while(scanf("%d%d",&a,&n)!=EOF)
{
head1=0,head2=0,tail1=1,tail2=1,cnt=2,las=a;
q1[tail1]=2*a+1;
q2[tail2]=3*a+1;
while(tail1//这个只是因为不知道while写成什么才这样的(其实写成while(true)就可以里)
{
x=min(q1[head1+1],q2[head2+1]);
while(x==las)//把重复的队首一直删直到不重复
{
if(x==q1[head1+1])
head1++;
else
head2++;
x=min(q1[head1+1],q2[head2+1]);
}
if(cnt==n)//单调队列嘛,队首是第n个说明他就是第n小的
{
cout<'\n';
break;
}
cnt++;
if(q1[tail1]!=2*x+1)//其实前面判过队首了就不需要判这个了,直接加就可以
q1[++tail1]=2*x+1;
if(q2[tail2]!=3*x+1)
q2[++tail2]=3*x+1;
las=x;
}
}
return 0;
}
本题结。