Description
现有N*(N+1)/2 个人围成一圈,编号从1到N*(N+1)/2,其中编号i与编号i+1的人相邻,编号N*(N+1)/2与编号1相邻。如今,我们要执行N-1个阶段的杀人仪式,在第一个阶段开始前,从编号为1的人开始报数。在第i阶段中,报到n+1-i这个数的人要被杀害,之后从被杀害者的下一个相邻的人开始从1开始报数,在第i个阶段里需要杀死n+1-i个人,随后即可进入下一个阶段,更具体地说:
在第1个阶段中,报数为N的人会被杀害,在这个阶段中需要杀死N个人;
在第2个阶段中,报数为N-1的人会被杀害,在这个阶段中需要杀死N-1个人;
.
.
.
在第n-1个阶段中,报数为2的人会被杀害,在这个阶段中需要杀死2个人。
仪式结束后,会剩下一个幸存者,请问这位幸存者的编号是多少?
Input
第一行一个整数T,表示数据的组数。
接下来T行,每行一个整数N,N定义如题目所述。
Output
输出一个整数,表示幸存者的编号
Sample Input
3
1
2
3
Sample Output
1
3
2
Data Constraint
20%数据,1<=n<=100,T <=10
40%数据,1<=n<=100,000,T<=10
100%数据,1<=n<=5,000,000,T<=100000
Solution
好难的找规律题啊,ヾ(。ꏿ﹏ꏿ)ノ゙,不看题解只有暴力想法。。。
首先我们可以发现,当把第一阶段的数删了之后,原来的
n∗(n+1)2
n
∗
(
n
+
1
)
2
变成了
n∗(n−1)2
n
∗
(
n
−
1
)
2
个,而且,后面删除的和n-1时相似,就可以利用n-1时的结论了。
比如说(来自题解)
我们设f[i]表示当i=n时,存活的人的编号是f[i]
f[4] = 10
现在要求f[5]
我们将他们列出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
第一轮死掉的人是5 , 10 , 15 , 6 , 12
剩下的数是
1 2 3 4 7 8 9 11 13 14
删掉12之后我们就是从13开始的
即现在的数列是
13 14 1 2 3 4 7 8 9 11
那么这一个数列的第10个数就是f[5]即11
所以,要知道最后一个在第一阶段删的数,即那些数已经删掉(计算时跳过)
可以列几个数找出删掉数的规律
1.当n为偶数时,删的数依次为
n,2n,3n,....(n/2)*n
n/2,n/2+(n+1),n/2+2(n+1),....n/2+(n/2-1)*(n+1)
2.当n为奇数时,删的数依次为
n,2n,3n,....(n+1)/2*n
n+1,2(n+1),....(n-1)/2*(n+1)
然后,往后走f[n-1]个数(跳过被删掉的),注意有可能从n*(n+1)/2走到1,而且要开longlong记录。
蒟蒻写的比较慢(而且很丑[捂脸]),超时了,且不会卡常,只能开o2卡时间了。。。T_T
#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 5000050
#define M 5000000
#define LL long long
LL f[N];
int ti,n;
LL min(LL x,LL y)
{
if (x<y) return x;
else return y;
}
__attribute__((optimize("-O3")))
LL js(LL l,LL r,LL t)
{
LL sl,sr;
if (t%2==0)
{
sl=l/t+(l-t/2+t+1)/(t+1);
sr=r/t+(r-t/2+t+1)/(t+1);
}
else
{
sl=l/t+l/(t+1);
sr=r/t+r/(t+1);
}
return sr-sl;
}
__attribute__((optimize("-O3")))
void pre()
{
f[1]=1;
LL x,y,z,w;
LL last;
for (LL i=2;i<=M;i++)
{
if (i%2==0)
{
z=i/2+(i/2-1)*(i+1);
x=i/2*i; y=i/2;
}
else
{
z=i/2*(i+1);
x=(i+1)/2*i; y=i+1;
}
last=f[i-1];
if (x-z>last)
{
f[i]=z+last;
continue;
}
last+=1;
if (i*(i+1)/2-z>=last)
{
f[i]=z+last;
continue;
}
last-=i*(i+1)/2-z;
z=0;
while (js(z,z+last,i)>0)
{
w=z+last;
last=js(z,z+last,i);
z=w;
}
f[i]=z+last;
}
}
int main()
{
pre();
scanf("%d",&ti);
for (int ii=1;ii<=ti;ii++)
{
scanf("%d",&n);
printf("%lld\n",f[n]);
}
return 0;
}