2021浙江省赛 F I

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

那么通过$\lceil \frac{19}{11} \rceil=2$可以发现倍数为2,那么19在倍数为2的情况下可以使用的最小的a为$\lceil \frac{19}{2} \rceil=10$

也就是说a取\left [ 10,11 \right ]对应的倍数都为2,那么此时取10一定是最优的,更新一下答案

   可以发现取11,那么a的操作次数为0,b的操作次数为3,一共为3

                 取10的话,a的操作次数为1,b的操作次数为1,一共为2(变化趋势对应上方蓝字)

接下来a取L-1也就是9,重新计算一下可以发现$\lceil \frac{19}{9} \rceil=3$.此时最小可用的a为$\lceil \frac{19}{3} \rceil=7$

那么a取\left [ 7,9 \right ]对应的倍数都是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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值