其他分享
首页 > 其他分享> > [游记]2022年多校冲刺NOIP联训测试4-2022.7.22

[游记]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