根据题目可以退出这个式子:S*b^k+a*b^m = T(m<=k)。
然后我们就可以枚举k,再倍增的使得m尽量小(k就是乘的次数,m就是加的次数)。
时间O(log^2 T)。

#include<bits/stdc++.h> #define LL long long #define re register #define INF 2100000000 using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int S=read(),T=read(),a=read(),b=read(); LL ans=INF; LL n=0,nowb=1;; while(1) { LL tt=T-S*nowb; if(tt<0)break; if(tt%a==0) { LL m=tt/a; LL bb=nowb; LL tot=n; while(m) { tot+=m/bb; m%=bb; bb/=b; } ans=min(ans,tot); } n++; nowb*=b; } if(ans==INF)printf("-1 "); else printf("%lld ",ans); } /* 2 3 1 2 3 50 1 2 1 40 4 4 1 1000 23 2325 1234270 100000000 20 5778 */
这道题一开始就想错了哭。
看这样一组数据:
8 3
2 6
2 7
3 6
下意识地就会觉得连3 6是最优的,那么最大长度是2。
但是应该是连2 6,最大长度为1。(3,6)可以是从6到2再到3。
现在看正解怎么做:
二分答案显然。
对于一个点对(x,y)如果超过了二分的mid,一定要找到一个点对(u,v)使得|x - u| + |y - v|小于等于mid。
这个公式就是曼哈顿距离了。
那么我们把(x,y)和(u,v)都抽象为一个点,(u,v)要在(x,y)的曼哈顿距离内。
如果要更改的点对的范围都有交的话return true,否则return false。
那么如何求交呢?这里有一个小技巧。
可以把其转化到y+x和y-x上,就变成了正着的正方形。
然后就是求交:(lx,rx)是x的范围,(ly,ry)是y的范围。

#include<bits/stdc++.h> #define LL long long #define N 100003 #define INF 2100000000 using namespace std; LL read() { LL x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } struct EDGE{ int l,r,id; }w[N]; int n,m; int b[N]; int tot=0; int tmp[N],dep[N]; pair<int,int> trans(pair<int,int> a) { return make_pair(a.first-a.second,a.first+a.second); } bool check(int mid) { int lx=-INF,rx=INF,ly=-INF,ry=INF; for(int i=1;i<=m;++i) { if(w[i].r-w[i].l<=mid)continue; pair<int,int>a=trans(make_pair(w[i].l+mid,w[i].r)); pair<int,int>b=trans(make_pair(w[i].l-mid,w[i].r)); lx=max(lx,b.first),rx=min(rx,a.first); ly=max(ly,b.second),ry=min(ry,a.second); if(lx>rx||ly>ry)return 0; } return 1; } int main() { freopen("b.in","r",stdin); freopen("b.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;++i)dep[i]=i; for(int i=1;i<=m;++i) w[i].l=read(),w[i].r=read(),w[i].id=i; int l=0,r=n+10; int ans; while(l<r) { int mid=(l+r)>>1; if(check(mid))ans=mid,r=mid; else l=mid+1; } printf("%d ",ans); } /* 10 3 1 5 3 8 4 10 */
因为最后要使得每个位置与位置上的数一一对应。
我们考虑怎么使得最快达到每个位置与位置上的数一一对应。
每次对于位置i,如果在其位置的数是a[i]而不是i,就把数字 i 换到 i 这个位置上,把a[i]换过去。
分析一下我们可知,B操作一次A操作一次和B操作多次A再操作是等价的。(反正都是换数)。
不如二分出B操作的次数mid,然后看换到最终结果A的操作次数会不会超过mid。

#include<bits/stdc++.h> #define LL long long #define N 200003 using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } int X[N],Y[N],a[N],b[N],vis[N]; int n; bool check(int mid) { for(int i=1;i<=n;++i)a[i]=b[i]; for(int i=1;i<=mid;++i) swap(a[X[i]],a[Y[i]]); int cnt=0; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;++i) { if(vis[i])continue; vis[i]=1; int j=i; while(!vis[a[j]]) { cnt++; j=a[j]; vis[j]=1; } } return cnt<=mid; } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); n=read(); for(int i=1;i<=n;++i)b[i]=a[i]=read()+1; for(int i=1;i<=2*n;++i) X[i]=read()+1,Y[i]=read()+1; int l=0,r=N; int ans; while(l<r) { int mid=(l+r)>>1; if(check(mid))ans=mid,r=mid; else l=mid+1; } printf("%d ",ans); } /* 3 1 2 0 1 2 0 2 0 0 0 1 0 2 1 2 3 1 0 2 1 2 0 2 0 0 0 1 0 2 1 2 */