先讲解D1
当目前剩下的数字的数目为p的时候 一次操作就会减少p/k个元素 因为操作次数少 我们可以直接进行计算 我们可以用二分 枚举第k个数字 判断是否合法 如果x次操作后剩余的数目大于等于k就合法 知道找到等于k的位置:
代码如下:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int x,y,k;
cin>>x>>y>>k;
int l=1,r=1e12+1;
while(l<r){
int mid=(l+r)>>1;
int tmp=mid;
for(int i=1;i<=x;i++){
tmp-=tmp/y;
}
if(tmp>=k){
r=mid;
}else l=mid+1;
}
if(r==1e12+1){
cout<<-1<<'\n';
return ;
}else {
cout<<r<<'\n';
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)solve();
return 0;
}
D2
由于这次x的范围很大 所以我们无法进行x次模拟计算
但是我们可以根据操作后的数字反向推导操作前的数字
p'=p-p/y 我们可以直观的想象一下: p个数字 分为y块完整块 可以分p/y块 进行一次操作后 每一块都少了一块 也就是如果分为y-1块 不计算剩余的部分的话 块数不变 剩余部分如果刚好相等 可以-1 也就是(p'-1)/(y-1)=p/y
那么有p=p'+(p'-1)/(y-1)
多次操作中(p-1)/(y-1)可能不变 可以批量操作
令c=(k-1)/(y-1) 多次操作中c不变 我们计算c=c+1时的区间左端点 也就是(c+1)*(y-1)+1
cnt=(fk - k + c - 1) / c(向上取整);
每次批量操作cnt次即可
代码实现如下:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int INF=1e12;
void solve(){
int x,y,k;
cin>>x>>y>>k;
if(y==1){
cout<<-1<<'\n';
return;
}
for(int i=0;i<x;){
int cur=(k-1)/(y-1);
if(cur==0){
break;
}
int nxt=(cur+1)*(y-1)+1;
int cnt=(nxt-k+cur-1)/cur;
cnt=min(x-i,cnt);
k+=cnt*cur;
if(k>INF){
cout<<-1<<'\n';
return ;
}
i+=cnt;
}
cout<<k<<'\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)solve();
return 0;
}