F - Fair Distribution
题意
给定两个a,b,每次操作可以使b++或者a--,为使得b是a的倍数,最小操作次数是多少
思路
朴素版本:枚举a一直到1为止,每一次找到离b最近的a的倍数(>=b),然后每次更新一下最小操作次数是多少,时间复杂度O(n*T)
优化版本:每一个a在找到:找到离b最近的a的倍数(>=b) 的时候都会求到这个倍数(>=1),利用这个倍数可以找到满足这个倍数条件之下的最小可能的a,假设满足这个倍数的区间为[L,R],那么a取L一定是最优,因为这个倍数>=1,a如果-1那么b对应的数字就要+1×倍数,接下来求出l-1对应的倍数,然后以此类推
举个例子,假设此时输入样例为
11 19
那么通过可以发现倍数为2,那么19在倍数为2的情况下可以使用的最小的a为
也就是说a取对应的倍数都为2,那么此时取10一定是最优的,更新一下答案
可以发现取11,那么a的操作次数为0,b的操作次数为3,一共为3
取10的话,a的操作次数为1,b的操作次数为1,一共为2(变化趋势对应上方蓝字)
接下来a取L-1也就是9,重新计算一下可以发现.此时最小可用的a为
那么a取对应的倍数都是3,那么取7一定最优,再更新一下答案
以此类推,每次更新一下答案
代码
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define u1 (u<<1)
#define u2 (u<<1|1)
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define sd(x) scanf("%Lf",&x)
#define sdd(x,y) scanf("%Lf %Lf",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _pre(i,a,b) for(int i=(a);i>=(b);--i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=1e6+10,INF=4e18;
int n,m;
void solve()
{
cin>>n>>m;
if(n>m)
{
cout<<n-m<<'\n';
return;
}
if(m%n==0)
{
cout<<"0\n";
return;
}
int res=INF,cnt=0;
int bei=(m+n-1)/n;
while(1)
{
cnt++;
int now=(m+bei-1)/bei;//n最小需要达到
if(n-now>res)break;//剪枝
res=min(res,n-now+now*bei-m);
if(now==1)break;
bei=(m+now-1-1)/(now-1);
}
cout<<res<<'\n';
return ;
}
signed main()
{
IOS;
int T=1;
cin>>T;
while(T--)
solve();
return 0;
}
I - Grammy and Ropes
题意
给定图中蓝色点的覆盖状态,问有多少种不同方法选择其中的几个绳子剪断(也可以不剪)使得绳子相互分离
思路
可以初步发现,绳子两两之间的状态基本上都是独立的,也就是1,2号绳子的是否相交只取决于1 4两个蓝点,1,3号绳子只取决于2 5,2,3号绳子只取决于3 6,那么把绳子的相交状态累加起来,就可以初步找到答案
后来发现wa7了,于是手撕了三个圆环又找了几种状态,发现1覆盖2,2覆盖3,3覆盖1这种情况虽然绳子两两是覆盖关系,没有相交,但是这种覆盖关系会使三个圆环缠在一起,于是在这种情况下可以选择随意选择剪一根绳子使绳子可以拉开,这里的方案是七种,详情见代码
代码
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define u1 (u<<1)
#define u2 (u<<1|1)
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define sd(x) scanf("%Lf",&x)
#define sdd(x,y) scanf("%Lf %Lf",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _pre(i,a,b) for(int i=(a);i>=(b);--i)
#define all(x) (x).beg`in(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
const int N=1e6+10,INF=4e18;
int n,m;
bool bl[10];
void solve()
{
_rep(i,1,6)
{
string s;
cin>>s;
if(s=="true")bl[i]=true;
else bl[i]=false;
}
string s="";
_rep(i,1,3)
{
if(bl[i])s+='1';
else s+='0';
}
int a=0;
if(bl[1]!=bl[4])a++;
if(bl[2]!=bl[5])a++;
if(bl[3]!=bl[6])a++;
if(a==0)
{
if(s=="010"||s=="101")cout<<"7\n";//特判相互覆盖的情况{1,2,3,12,13,23,123}
else cout<<"8\n";//答案集合{空集,1,2,3,12,13,23,123}
}
else if(a==1)cout<<"6\n";//假设1,2相交,答案集合{1,2,12,13,23,123}
else if(a==2)cout<<"5\n";//假设1,2相交且1,3相交,答案集合{1,23,12,13,123}
else cout<<"4\n";//两两相交,答案集合{12,13,23,123}
return ;
}
signed main()
{
IOS;
int T=1;
// cin>>T;
while(T--)
solve();
return 0;
}