[ABC195B]Many oranges

目录

壹、题目描述 ¶

传送门 to Atcoder

橘子太多了,它红得就像 \(\color{red}{\text{WA}}\).

贰、题解 ¶

单位还不一样,得把 \(W\) 换算成 \(\rm g\) 作单位,所以真正的 \(w=1000W\).

你或许可以直接暴力做背包,但是由于最多可能要选 \({w\over a}\le 10^6\) 个,所以最后的复杂度可能会是 \(\mathcal O(10^6\times 10^6\times 10^3)=\mathcal O(10^{15})\),这个复杂度还是算了吧......

但是你会发现个数好像可以二分,但是也只不过给其中一个 \(10^6\) 加上一个 \(\log\),似乎并没有什么用。

我们从另外的角度考虑这个问题,不要老想着背包。

先考虑如何满足每个橘子都在 \([a,b]\) 的范围中,首先,每种方案的橘子得有个整数部分吧,那么我们就枚举这个整数部分的重量 \(x\),得到这个重量之后,我们还缺什么呢?或许什么都不缺了吧,因为我们得到 \(x\),就可以算出橘子个数 \(\lfloor{w\over x}\rfloor\),并且算一下余数 \(w\pmod{x}\) 平摊到每个橘子上面是否会爆出 \(b\).

但是这个 \(\lfloor{w\over x}\rfloor\) 真的是我们想要求的吗?这只是针对这个整数的最大橘子个数,并不一定就是最终的橘子个数,所以我们还不能直接使用 \(\lfloor{w\over x}\rfloor\) 作为个数,而是再添加一个循环枚举个数

然后,我们就有了一个 \(\mathcal O(10^8)\) 的算法。

但是这可以做到 \(\mathcal O(1)\):

为了达到 “最多”,我们考虑尽量多地使用 \(a\),但是这里有个问题 —— 我们可能会剩下一点,而这一点就要摊在我们所有的 \(k=\lfloor{w\over a}\rfloor\) 个橘子上,这 \(k\) 个橘子,我们能最多可以分配 \(k(b-a)\) 个,如果剩下的重量超过了那么多,显然无解了,因为这是我们所能构造的,每个橘子最轻的方案,并且不难发现,这也是橘子数量最多的方案。

考虑完最多,我们来想一想最少,情况十分类似 —— 考虑使用尽可能多的 \(b\),但是同样,我们可能会剩下一些重量,这又怎么处理?同样的,设 \(l=\lfloor{w\over b}\rfloor\),如果没有余数,显然最少橘子就是 \(l\),但是如果有剩下,最少橘子就肯定 \(l+1\) 了,何也?我们剩下的橘子一定是 \(<b\) 的,说明我们新增加的橘子是不会超过上界,但是下界 \(a\) 是否能够达到?或许不能,但是我们可以从重量为 \(b\) 的橘子中分一些给它,让它达到下界,但是或许分了一些之后都还达不到,不过这种情况不可能存在,首先,我们在计算下界的时候判断了是否有解,另外,如果 \(l\) 无解,那么一定是 \(l<k\),对于 \(l+1\) 就有 \(l+1\le k\),使用 \(k\) 个橘子都可以上所有橘子达到下界,那么使用小于等于 \(k\) 个橘子不是更容易达到下界?

综上,最后的答案就是 \(l(\text{or }l+1),k\) 了。

叁、参考代码 ¶

首当其冲的是 \(\mathcal O(10^8)\) 的代码谁不喜欢暴力呢?

#define Endl putchar('\n')
#define mp(a, b) make_pair(a, b)
#define fi first
#define se second
typedef long long ll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline T readin(T x){
	x=0; int f=0; char c;
	while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
	for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
	return f? -x: x;
}

const int inf=0x3f3f3f3f;

int a, b, w;

signed main(){
	a=readin(1), b=readin(1), w=readin(1)*1000;
	int ans1=inf, ans2=-1, cnt, res;
	for(int i=a; i<=b; ++i){
		cnt=w/i, res=w%i;
		if(cnt==0) break;
		for(int j=1; j<=cnt; ++j){
			if(i*cnt+res<=b*j)
				ans1=min(ans1, j), ans2=max(ans2, j);
		}
	}
	if(ans2==-1) printf("UNSATISFIABLE\n");
	else printf("%d %d\n", ans1, ans2);
	return 0;
}

然而这是 \(\mathcal O(1)\) 的代码:

int a, b, w;

signed main(){
	a=readin(1), b=readin(1), w=readin(1)*1000;
	int k=w/a, l;
	if(w%a>k*(b-a)) return printf("UNSATISFIABLE\n"), 0;
	l=w/b+(w%b? 1: 0);
	printf("%d %d\n", l, k);
	return 0;
}

我是大**。

肆、游戏体验 ¶

有的时候,如果我们能够判断有解,就可以大胆地放开自己地脑子,让答案自己去调整吧。

上一篇:莫比乌斯反演学习笔记


下一篇:题解[SDOI2014]数表