P7777 『JROI-2』Shelter

这里提供一个 O(TlogN)O(T\log N) 的二分做法。

为了使代价最小,对于第一种抓取方式,我们尽量取较小的数;对于第二种抓取方式,我们尽量取相邻的数。

于是,我们要找到一个数 kk,使 1k11\sim k-1 的数用第一种抓法,knk\sim n 用第二种抓法,且代价最小。这具有单调性,于是考虑二分。

首先是边界,rr 自然是 nn,但因为第二种抓法的个数是偶数,所以要保证 llnn 奇偶性相同,因此 ll 必须为 n and 1n\ \text{and}\ 1

接下来就考虑一个 midmid 是属于哪一种了。

  • 如果 midmidnn 奇偶性相同,那么与 midmid 相邻的数就为 mid1mid-1

  • 如果 midmidnn 奇偶性不同,那么与 midmid 相邻的数就为 mid+1mid+1

以上规律可以通过打表发现,总结一下就有了通项判定式 ((mid+mid1+2×((n and 1)(mid and 1)) 0)×p<q((mid+mid-1+2\times((n\ \text{and}\ 1)-(mid\ \text{and}\ 1))\neq\ 0)\times p<q

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t;
ll n,p,q,ans,l,r,mid;
ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(ll X){
	if(X<0) {putchar('-'); X=~(X-1);}
	int s[21],top=0;
	while(X) {s[++top]=X%10; X/=10;}
	if(!top) s[++top]=0;
	while(top) putchar(s[top--]+'0');
}
int main()
{
    t=read();
    while(t--)
    {
    	n=read();
    	p=read();
    	q=read();
    	l=n&1,r=n;
    	while(l<r)
    	{
    		mid=(l+r+1)>>1;
    		if((mid*2-1+2*((n&1)-(mid&1)!=0))*p<q)
    			l=mid;
    		else
    			r=mid-1;
		}
		write(l*(l+1)/2*p+(n-l)/2*q);
		putchar('\n');
	}
	return 0;
}

当然,细心 ctj 的你一定会发现,这个代码其实并不能通过本题,原因是本题原来时限为 2s,无优化可以轻松过。

出题人为了只让 O(T)O(T) 的算法过,把时限改成了 500ms,不过这个算法加上快读也能过。

出题人仍不死心,把时限调成了 300ms,不过道高一尺,魔高一丈,不过再加上快写等优化仍等稳过,开 O2 后吊打出题人

相关帖子

posted @ 2021-08-05 22:08  luckydrawbox  阅读(92)  评论(0)    收藏  举报  来源