每天都坚持写写
说实话很早就想写这道题了,但是一直不知道怎么解释那个算法,做完之后也看过聚聚的题解,说有一个什么数学结论,然而我研究了这么久都没有研究出什么数学结论,只能再试着看看了。虽然之前已经有了这道题的题解,但我这个做法估计并不是标准做法,而且还是挺快的,所以也就写一写了。
第一眼看到这道题的时候还是挺头疼的,看着这么巨大的数字,不知道哪里是突破口,找了一阵发现一个地方不错——k,因为k的值不是特别大所以感觉每次遍历一遍都可以消灭数量可观的数字,可以这么理解,每次数量都会乘以(k - 1) / k(当然因为n比k小的时候肯定要取模,所以那时候至少干掉一个,所以其实会比预期更快),这样估算一下每次的计算量肯定实在百万级。那么就这么思考了。
首先说一句,我这个方法太抽象了所以我也不保证一定讲的清楚。考虑每次的减少,会发现这次的数字和之前的数字是一个映射的关系,可以不断地映射从而使得这个值从每层递进从而确定在第一层的编号,当然存在两种映射,n比k大和n比k小的情况,这两种情况分别用一个编号判断,这样就可以快速解决这个问题了。
至于优化只做了一个地方就是k为1的情况,这个可以快速求解。
贴代码(估计没什么人看的懂。。。)
# include <stdio.h>
# include <algorithm>
using namespace std;
const int MAX_T = 20;
const int MAX_P = 200000;
typedef pair<int , int> P;
int N, K, T;
int J[MAX_T];
P sea[MAX_T], box[MAX_P], Q[MAX_T];
int pum, tum;
void solve()
{
int i, j;
if(K == 1)
{
for(i = 0 ; i < T ; i++)
printf("%d%s", Q[i].first, (i == T - 1) ? "\n" : " ");
return;
}
sort(Q , Q + T);
for(i = 0 ; i < T ; i++)
{
J[Q[i].second] = i;
}
int yu = 0, res = N, sum = 0;
pum = tum = 0;
while(res > 0 && tum < T)
{
int at, bt;
int nres, nyu, nsum;
if(K > res)
{
bt = 0;
int nK = K % res;
nK -= yu;
at = nK % res;
if(at <= 0)
at += res;
nres = res - 1;
nyu = res - at;
nsum = sum + 1;
}
else
{
bt = 1;
int pe = (yu + res) / K;
nres = res - pe;
nyu = (yu + res) % K;
nsum = sum + pe;
at = yu;
}
box[pum++] = P(at , bt);
//printf("%d %d\n", at, bt);
while(Q[tum].first <= nsum && tum < T)
{
if(bt == 0)
{
sea[tum++] = P(at , pum - 1);
}
else
{
int yy = Q[tum].first - sum;
sea[tum++] = P(yy * K - yu , pum - 1);
}
}
res = nres;
sum = nsum;
yu = nyu;
}
for(i = 0 ; i < T ; i++)
{
int ai = sea[J[i]].first, bi = sea[J[i]].second;
//printf("%d %d\n", ai, bi);
for(j = bi - 1 ; j >= 0 ; j--)
{
if(box[j].second == 0)
{
if(ai >= box[j].first)
ai++;
}
else
{
ai = ai + (ai + box[j].first - 1) / (K - 1);
}
}
printf("%d%s", ai, (i == T - 1) ? "\n" : " ");
}
}
int main()
{
while(~scanf("%d %d %d", &N, &K, &T))
{
int i;
for(i = 0 ; i < T ; i++)
{
scanf("%d", &Q[i].first);
Q[i].second = i;
}
solve();
}
return 0;
}