悲报:复活赛没打赢。
2020kanade · · 题解
寄喽,CSP-S 2023 只会这一道,场外写的,还惨遭卡常,真进场了大概分数还要折半。
思路很粗暴,当
因此,对于树
using i128=__int128;
i128 calc(i128 x,i128 b,i128 c,i128 y=1e9)
{
if(c==0) return (y-x+1)*b;
else if(c<0)
{
i128 z=(-b)/c;
if(z*c+b==0) --z;
if(y<=z) return (y-x+1)*(b+c*x+b+c*y)/2;
if(x>z) return y-x+1;
return (z-x+1)*(b+c*x+b+c*z)/2+y-z;
}
else
{
return (y-x+1)*(b+c*x+b+c*y)/2;
}
}
问题在于,种树的决策过于复杂,直接推过去貌似不太可做,注意题目的提示 “所有答案均不超过
那么,设当前二分值为
那么,考虑一个贪心:每次去撅时限最小的点。正确性证明的话,直接考虑如果时限最小的点都得不到满足那么其他点一定无法满足,至于有多个点先撅哪个,答案是无所谓,考虑对撅这个点集的代价求和,这个和是一定的(因为一定是撅了若干条根到某个结点的路径上全部结点,这些路径的交集一定),只要这个和超了时限那无论内部怎么调整顺序都一定不合法,反之亦然。同理,也可以从此方面证明全局贪心策略的正确性。
就这些,结束了。具体实现的话,考虑最粗暴的一套:直接拿个堆按照最晚被撅的时刻维护所有结点,再开个桶标记被撅的点,最晚被撅时刻刚才说过了直接二分求,撅点的话就暴力往上跳父亲同时一路标记直到跳到被标记的点或者跳出整棵树,同时一边跳一边计时刻,一旦超了当前点时限或者当前二分的限制时限直接返回假。不用特别处理根,第一个肯定撅她。
时间复杂度 __int128 与瞎开数据结构 (比如拿 std::set 当堆使),笔者拿到了 40 分的高分。更可喜可贺的是,由于脑瘫笔者滥用某数据类型导致该写法卡常拉满也只能在数据更强的云斗搞个令人眼前一黑的 75 分。
因此,考虑优化时间复杂度。
首先,求每个点的被撅最晚时刻不需要使用二分,考虑本质是等差数列求和加上一个斜率为
考虑等差数列求和公式本质是二次函数,问题转化为求一元二次不等式的最大正整数解问题,按照前面写的
实现出来可能长这样:
using i128=__int128;
using lld=long double;
int calc2(i128 x,i128 a,i128 b,i128 c)
{
if(c==0) return x+1-ceil((long double)((long double)a/(long double)b));
else if(c<0)
{
i128 z=(-b)/c;
if(z*c+b==0) --z;
if(z<x&&calc(z+1,b,c,x)>=(i128)a)
{
return x-a+1;
}
else if(z<x)
{
a-=calc(z+1,b,c,x);
x=z;
i128 C=c*x*x+(2*b+c)*x+2*(b-a),A=-c,B=c-b*2;
lld dlt=sqrt((lld)(B*B-4*A*C));
lld a1=(dlt-B)/(2*A),a2=(-B-dlt)/(2*A);
if(a1>0.5&&a2>0.5) return floor(min(a1,a2));
else if(a1>0.5) return floor(a1);
else return floor(a2);
}
else
{
i128 C=c*x*x+(2*b+c)*x+2*(b-a),A=-c,B=c-b*2;
lld dlt=sqrt((lld)(B*B-4*A*C));
lld a1=(dlt-B)/(2*A),a2=(-B-dlt)/(2*A);
if(a1>0.5&&a2>0.5) return floor(min(a1,a2));
else if(a1>0.5) return floor(a1);
else return floor(a2);
}
}
else
{
i128 C=c*x*x+(2*b+c)*x+2*(b-a),A=-c,B=c-b*2;
lld dlt=sqrt((lld)(B*B-4*A*C));
lld a1=(dlt-B)/(2*A),a2=(-B-dlt)/(2*A);
if(a1>0.5&&a2>0.5) return floor(min(a1,a2));
else if(a1>0.5) return floor(a1);
else return floor(a2);
}
}
如此可将时间复杂度降低至
此时内层还有一个瓶颈是排序。
我会基数排序!
直接每个数每五位作为一个关键字,内层用计排,非常好线性排序算法,使我的计算机旋转,奖励一佰昏!
什么?你没被宇宙射线击中过,不会基数排序?
写不出优秀高贵的
我知道你很急,但你先别急,注意一个性质:一棵树被撅完的总时间是固定的
那么,最晚被撅时刻大于
现在值域降低到
这种写法常数也更小,实际实现也更容易,是更为优秀的
最后,祝天下所有人不被卡常,也祝我早日适应 XCPC,这玩意儿要是放 XCPC 我搞不好就因为卡常饮恨西北了。