A 招待
本题是一个三进制下的模拟题。为什么判定这是三进制?是因为砝码的重量一定是3的幂次。
假如砝码的重量都是2的幂次,那么根据二进制的相关知识可以知道110=4+2+0=6,也就是6可以由一个2单位的砝码和一个4单位的砝码称量。换成三进制的的话,对于数字13,三进制表示是111,也就是1+3+9,重量为13的物体可以由1,3,9这三个重量的砝码称量。但是三进制表达式中含有2这个数字,题给的每种重量的砝码只有一个,那这要怎么办呢?
因为可以在天平的物体盘里添加砝码,所以只需要通过添加配重块把物品盘的重量配成三进制下只有0和1两个数字的情况即可。所以对于表达式里含有2这个数字的某个值,比如说,52的表达式就是1221,当你在物品盘里加上一个重量为3的砝码时,根据进位,表达式就会变成2001,正好是55,再添加一个重量为27的砝码,表达式变为10001,十进制是82,这样这个数字就可以由81和1直接表示了。
实现这个思路直接模拟即可。
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll digits[50000],left1[50000],right2[50000];
ll cnt1 = 0, cnt2 = 0;
ll quickpower(ll a, ll b, ll mod)
{
ll ans = 1;
a %= mod;
while(b)
{
if ((b & 1) > 0)
ans = (ans*a) % mod;
b >>= 1;
a = (a*a) % mod;
}
return ans;
}
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll n;
cin >> n;
ll tmp = n;
ll cnt = 0;
while (n > 0)
{
digits[++cnt] = n % 3;
n /= 3;
}
for (int i = 1; i <=2*cnt; i++)
{
if (digits[i] == 2)
{
ll next = i + 1;
while (digits[next] + 1 > 2)
{
digits[next] = (digits[next]+1)%3;
next++;
}
digits[next] += 1;
right2[++cnt2] = i;
}
else if (digits[i] == 1)
left1[++cnt1] = i;
}
sort(left1 + 1, left1 + 1 + cnt1);
sort(right2 + 1, right2 + 1 + cnt2);
for (int i = 1; i <= cnt1; i++)
{
if (i == cnt1)
{
cout << quickpower(3, left1[i] - 1, LLONG_MAX) << endl;
}
else
cout << quickpower(3, left1[i] - 1, LLONG_MAX)<<" ";
}
cout << tmp << " ";
for (int i = 1; i <= cnt2; i++)
{
if (i == cnt2)
{
cout << quickpower(3, right2[i] - 1, LLONG_MAX) << endl;
}
else
cout << quickpower(3, right2[i] - 1, LLONG_MAX)<<" ";
}
return 0;
}
B 小说
很明显的分层图最短路,但是这个题的意思是要求K次免费后最短路上的路径最大值,所以需要修改一下模板,把最短路算法里边求距离的换成求路径中的最大值即可。
struct node
{
ll next, to, value;
}nodes[1140000];
ll heads[1002000];
ll cnt = 0;
void construction(ll from, ll to, ll value)
{
nodes[cnt] = node{ heads[from], to, value };
heads[from] = cnt++;
}
bool vis[1002000];
ll dis[1002000];
void spfa(ll st, ll limit1, ll limit2)
{
for (int i = 0; i <= limit1 * (limit2+3); i++)
{
vis[i] = false;
dis[i] = INF;
}
dis[st] = 0;
queueq;
q.push(st);
vis[st] = true;
while (!q.empty())
{
ll current = q.front();
q.pop();
vis[current] = false;
for (int i = heads[current]; i != -1; i = nodes[i].next)
{
ll t = nodes[i].to;
ll nxt = max(nodes[i].value, dis[current]);
if (dis[t] > nxt)
{
dis[t] = nxt;
if (!vis[t])
{
q.push(t);
vis[t] = true;
}
}
}
}
}
int DETERMINATION()
{
//std::ios::sync_with_stdio(false);
//std::cin.tie(0), std::cout.tie(0);
ll n, m, k;
lldin(n), lldin(m), lldin(k);
reset(heads, -1);
for (int i = 1; i <= m; i++)
{
ll tmp1, tmp2, tmp3;
lldin(tmp1), lldin(tmp2), lldin(tmp3);
for (int j = 0; j <= k; j++)
{
construction(j*n + tmp1, j*n + tmp2, tmp3);
construction(j*n + tmp2, j*n + tmp1, tmp3);
}
for (int j = 1; j <= k; j++)
{
construction((j - 1)*n + tmp1, j*n + tmp2, 0);
construction((j - 1)*n + tmp2, j*n + tmp1, 0);
}
}
spfa(1, n, k);
if (dis[k*n+n] == INF)
cout << -1 << endl;
else
cout << dis[k*n+n] << endl;
return 0;
}
D 小A进学校
本题涉及一个k进制转换与求末尾0的问题。
首先说一下关于求一个数的末尾0的个数,末尾0的个数意味着这个数是10的若干整数倍,而10拆分成因子就是2和5(根据算数基本定理分解后的表达式可以看出),就十进制而言,10以内的2的倍数比5的倍数多,所以这就需要讨论一下2与5在某个数中有多少个这样的因子,然后取最小值。
值得一说的是阶乘数求末尾0,因为阶乘是若干个自然数的乘积,所以需要一步一步的筛因子幂次。比如说对于10!,要求2的幂次,就需要先把10/2,得到5,再把5/2,得到2,再把2/2,得到1,总的数目就是8,这么做的意义是:第一步先把5-10这个范围内的2筛出来(10,8,6),再把2-5这个范围内的2筛出来(4),再筛0-2范围内的(2)
然而一个数转换成k进制之后怎么判断末尾0呢?我们知道进制转换必要的几步是:对一个数取余base,然后把这个数除以base。(比如说6转化为二进制,就是6%2=0,6/2=3,3%2=1,3/2=1,1%2=1,1/2=0---->110)而出现0的关键步骤就在于取余base的这一步,如果某个数是base的整数倍,换句话说,就是把原数唯一分解后的若干因子可以组成base,那就意味着必然会出现0。至于有多少个0,那就是需要判断它内部有多少个乘积为base的因子,比如说对于数字8转化为二进制,它内部有2的立方,可以支付三次对2的整除(进制转换取余后需要除base),三次之后就不存在2的因子,故8的二进制表达式有3个0.
所以说转化成K进制后的末尾0就是看原数的因子够完整支付(整除)K的对应因子几次,换句话说就是对应因子的幂次取最小值。
ll primes[1500000], power[1500000];
ll cnt = 0;
void factorization(ll x)
{
ll limit = (ll)sqrt(x) + 1;
for (int i = 2; i<=limit; i++)
{
if (x%i == 0)
{
primes[++cnt] = i;
while (x%i == 0)
{
power[cnt]++;
x /= i;
}
}
}
if (x > 1)
{
primes[++cnt] = x;
power[cnt]++;
}
}
ll split(ll x,ll b)
{
ll ans = 0;
while (x > 0)
{
ans += x / b;
x /= b;
}
return ans;
}
int DETERMINATION()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll n, k;
cin >> n >> k;
factorization(k);
ll ans = LLONG_MAX;
for (int i = 1; i <= cnt; i++)
ans = min(ans, split(n, primes[i]) / power[i]);
cout << ans << endl;
return 0;
}