[游记]2022年多校冲刺NOIP联训测试4-2022.7.22
作者:互联网
立字为证:如果再用 $const int eps$ 就砸掉自己的电脑!!!
A. 甲国的军队
B. 虚弱
C. 萨鲁曼的半兽人
D. 序列
嗯,因为 $const int eps=1e-10$ 送掉一道题的分数
开心至极
总分 $160/400$
截图不太好截,不搞了(
A. 甲国的军队
首先考虑到对于整个战斗过程,$\sum\limits_{i=1}^{n}a_i$ 个士兵是必死无疑的
每场战斗有 $b_i$ 个士兵参加,这些士兵不一定都死
所以我们考虑如何把这些士兵压榨出更多的剩余价值
那么显然地可以考虑到将据点按照攻打之后能活下的人排序,这样可以让参加下一场战斗的士兵最多
然后贪心即可,复杂度 $\Theta(Tn\log n)$
#include<cstdio> #include<cstring> #include<algorithm> #include<string> #define int long long #define WR WinterRain using namespace std; const int WR=1001000; struct City{ int a,b; }city[WR]; int n,ans; int read(){ int s=0,w=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-48; ch=getchar(); } return s*w; } bool cmp(City x,City y){ return x.b-x.a>y.b-y.a; } signed main(){ //freopen("army.in","r",stdin); //freopen("army.out","w",stdout); int t=read(); while(t--){ n=read(); for(int i=1;i<=n;i++) city[i].a=read(),city[i].b=read(); sort(city+1,city+1+n,cmp); int tmp; ans=tmp=0; for(int i=1;i<=n;i++){ ans+=max(city[i].b-tmp,(long long)0); tmp=max(tmp,city[i].b); tmp-=city[i].a; } printf("%lld\n",ans); } //fclose(stdin); //fclose(stdout); return 0; }View Code
B. 虚弱
人类迷惑行为大赏
大家可以注意到这个 $\operatorname{SB}$ 开了个 $eps$ 然后类型是 $\operatorname{int}$
然后呢?一整道题分挂没了
愉悦
言归正传,不妨设 $s$ 为 $a$ 的前缀和数组
题意可以化简成取一个实数 $x$ 使得 $\forall 0\leqslant j \leqslant i\leqslant n $ ,有 $\left\vert s[i]-s[j]-(i-j)x \right\vert \leqslant ans$ ,求 $ans$ 最小值
原式即为 $\left\vert (s[i]-ix)-(s[j]-jx) \right\vert \leqslant ans$ ,也就是说我们要求 $\max\limits_{i=0}^{n}{s_i-ix}-\min\limits_{i=0}^{n}{s_i-ix}$ 最小
这是一个关于 $x$ 的函数,显然是有一个单峰的,所以考虑二分 / 三分,每次求一个最大子段和
不妨设 $mid=\dfrac{l+r}{2}$ ,$f(i)$ 表示 $x=i$ 时的最大子段和
可以设 $midl=mid-(eps\times 0.1)$ ,$midr=mid+(eps\times 0.1)$
那么考虑求出 $f(midl)$ 和 $f(midr)$ 后进行比较,如果 $f(midl)<f(midr)$ 那么用 $midr$ 更新 $r$ ,否则用 $midl$ 更新 $l$
注意原题精度卡到了丧心病狂的地步,需要把精度开到 $10^{-12}$ ,类型开为 $long$ $double$ 才可能过
#include<cstdio> #include<cstring> #include<algorithm> #include<string> #define int long long #define double long double #define WR WinterRain using namespace std; const int WR=1001000; const double eps=1e-12; int n; double sum,a[WR]; int modu[WR]; double ans1,ans2; int read(){ int s=0,w=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-48; ch=getchar(); } return s*w; } double check(double x){ for(int i=1;i<=n;i++) a[i]=(double)modu[i]-x; double tmp1=0.0,tmp2=0.0; ans1=0.0,ans2=0.0; for(int i=1;i<=n;i++){ if(tmp1>eps) tmp1+=a[i]; else tmp1=a[i]; if(tmp1>ans1) ans1=tmp1; a[i]=-a[i]; } for(int i=1;i<=n;i++){ if(tmp2>eps) tmp2+=a[i]; else tmp2=a[i]; if(tmp2>ans2) ans2=tmp2; } return max(ans1,ans2); } signed main(){ freopen("weakness2.in","r",stdin); freopen("weakness.out","w",stdout); n=read(); double l=(double)WR,r=-(double)WR; for(int i=1;i<=n;i++){ modu[i]=read(); l=min(l,(double)modu[i]); r=max(r,(double)modu[i]); } while((r-l)>eps){ double midl=(l+r)/2-eps*0.1,midr=(l+r)/2+eps*0.1; if(check(midl)<check(midr)){ r=midr; }else{ l=midl; } } printf("%.6Lf",check(l)); //fclose(stdin); //fclose(stdout); return 0; }View Code
官方的题解是用的单调栈,这里借用了一下证明
好的
标签:ch,NOIP,22,int,double,eps,联训,WR,include 来源: https://www.cnblogs.com/WintersRain/p/16507958.html