题目链接
题目思路
首先,一个完全图本身就是一个联通分量。若我们把他变成两个连通分量,最少要把某点和其他点的所有边都去除掉,
也就是要去掉n-1条边。若我们把他变成三个连通分量,在剩下n-1个结点的连通分量上,最少要把某点和其他点的所有边都去除掉,
也就是要去掉n-2条边,以此类推…我们发现规律后就很简单了,显然m越大,最后的答案也会越大,其具有单调性可以二分。
我们设答案为ans,那么ans肯定要满足(n-1)+(n-2)+…+(n-ans)<=m,左边可以直接求和,那么二分找到最大的ans即可。注意求和时
可能炸long long,因此我们要使用__int128,比赛中此题通过人数不是很多,估计就是被高精度坑了。
时间复杂度O(log(n))
——————————————————————————————————————————————————————
首先压缩下了一下范围暴力了一波,t了。后面发现直接二分就行qwq,但是发现炸了long long 然后就wa了。。。。。
后面发现__int128这个神仙东西
__int128不支持标准输入输出,要自己打板子 __int128的一般作用就是做中间值比较
后面想了想,其实可以long long 转double 然后也a了
这个题目最重要的意义就是如果long long炸了却又没有到高精度的地步可以__int128和double
__int128的代码
#include<cstring>
#include<cstdio>
#include<algorithm>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
using namespace std;
int t;
__int128 n,m;
ll a,b;
bool check(__int128 x){
return (2*n-1-x)*x/2<=m;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%lld %lld",&a,&b);
n=a,m=b;
if(m>=n*(n-1)/2){
printf("%lld\n",a);
}else{
ll l=0,r=n,ans=-1;
while(l<=r){
ll mid=l+(r-l)/2;
if(check(mid)){
ans=max(ans,mid+1);
l=mid+1;
}else{
r=mid-1;
}
}
printf("%lld\n",ans);
}
}
return 0;
}
double的代码
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
const double eps=1e-6;
int t;
ll n,m,ans;
bool check(ll x){
double sum=1.0*(2.0*n-1-1.0*x)*1.0*x/2;
return sum-m<=eps;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%lld %lld",&n,&m);
if(m>=1.0*n*(1.0*n-1)/2+eps){
printf("%lld\n",n);
}else{
ll l=0,r=n,ans=-1;
while(l<=r){
ll mid=l+(r-l)/2;
if(check(mid)){
ans=max(ans,mid+1);
l=mid+1;
}else{
r=mid-1;
}
}
printf("%lld\n",ans);
}
}
return 0;
}