CSP-S 模拟75

  不是一道题不会,是一道题都不会,全是暴力,T1高考数学,T2高斯消元早忘了,高考化学没学好,T3高考物理没学好

  

  

  导弹袭击 

    式子,设其他导弹的速度为(x,y),有用的导弹(a,b)必须满足 存在$\frac{A}{a}+\frac{B}{b}<=min(\frac{A}{x}+\frac{B}{y}) $

    直接照着式子打n^2做法,可以拿55~65分

    我们将$a=\frac{1}{a},b=\frac{1}{b},x=\frac{1}{x},y=\frac{1}{y}$,那么我们就是求 $Z=Aa+Bb$最小

    转换成函数形式$y=-\frac{A}{B}x+\frac{Z}{B}$ ,因为A,B为正实数,所以问题转化为求出是否存在一个斜率使得函数过$(a,b)$点的时候纵截距最小

    所以把$(x,y)$转换成二维坐标系上的点$(\frac{1}{x},\frac{1}{y})$,跑一个下凸包就可以了

    注意细节:跑凸包前先把所有不可能的点咕咕掉!  

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,st[310000],to[310000];
struct node{
    long double a,b;
    int id;
    bool operator < (const node x)const{
        return (a>x.a)||(a==x.a&&b>x.b);
    }
}w[310000];
long double cal(int x,int y){
    if(w[y].a==w[x].a) return 1;
    if(w[y].b==w[x].b) return 1;
    return w[x].a/w[x].b*w[y].a/w[y].b*(w[x].b-w[y].b)/(w[x].a-w[y].a);
}
int main(){
    //freopen("slay4.in","r",stdin);
    scanf("%d",&n);
    for(register int i=1;i<=n;i++) scanf("%LF%LF",&w[i].a,&w[i].b),w[i].id=i;
    sort(w+1,w+n+1);
    int num=0;
    for(register int i=1;i<=n;i++){
        if(w[i].a==w[i-1].a&&w[i].b==w[i-1].b){
            to[w[i-1].id]=w[i].id;
        }
        else{
            w[++num].a=w[i].a,w[num].b=w[i].b;
            w[num].id=w[i].id;
        }
    }
    n=num;
    sort(w+1,w+n+1);
    st[++st[0]]=1;
    for(register int i=2;i<=n;i++){
        if(cal(st[st[0]],i)>0) continue;
         while(st[0]>1&&cal(st[st[0]-1],st[st[0]])-cal(st[st[0]],i)>1e-30) st[0]--;
        st[++st[0]]=i;
    }
    num=st[0];
    for(register int i=1;i<=st[0];i++){
        st[i]=w[st[i]].id;
        for(register int j=to[st[i]];j;j=to[j])
            st[++num]=j;
    }
    st[0]=num;
    sort(st+1,st+st[0]+1);
    for(register int i=1;i<=st[0];i++) printf("%d ",st[i]);
}
View Code

  炼金术士的疑惑

    高斯消元

    读入很麻烦,但是其它挺简单的,主要是考察了对于高斯消元的理解

    将n个方程读入,第n+1个的询问读入,然后跑一遍高斯消元,高斯消元的过程就是将方程代入其他方程的过程,所以将n个方程消元的过程中代入询问方程并消去询问方程的系数,假设原来询问方程的焓变值为H,消元完之后会变成0,而现在初值是0,那么H就等于最后询问方程的答案的相反数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,tot;
char h[20];
const long long mod=233333;
const double eps=1e-6;
double a[210][210],b[210];
struct node{
    int ha[233333];
    int insert(){
        int len=strlen(h+1);
        long long sta=0;
        for(register int i=1;i<=len;i++){
            sta=(1ll*sta*33%mod+h[i])%mod;
        }
        if(!ha[sta]) ha[sta]=++tot;
        return ha[sta];
    }
}H;
void Gauss(){
    for(register int i=1;i<=n;i++){
        int p=i;
        for(register int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[p][i])) p=j;
        for(register int j=1;j<=tot+1;j++) swap(a[i][j],a[p][j]);
        if(fabs(a[i][i])<eps) continue; 
        double tmp=a[i][i];
        for(register int j=1;j<=tot+1;j++) a[i][j]/=tmp;
        for(register int j=1;j<=n+1;j++){
            tmp=a[j][i];
            if(i==j) continue;
            for(register int k=1;k<=tot+1;k++){
                a[j][k]-=a[i][k]*tmp;
            }
        }
    }
}
void init(int t){
    double x;
    while(1){
        scanf("%lf%s",&x,h+1);
        int sta=H.insert();
        a[t][sta]=x;
        scanf("%s",h+1);
        if(h[1]=='=') break;        
    }
    while(1){
        scanf("%lf%s",&x,h+1);
        int sta=H.insert();
        a[t][sta]=-x;
        scanf("%s",h+1);
        if(h[1]=='H') break;
    }
    if(t==n+1) return;
    scanf("%lf",&b[t]);    
}
int main(){
    scanf("%d",&n);
    for(register int i=1;i<=n;i++) init(i);
    n=max(n,tot);
    tot=n;
    init(n+1);
    for(register int i=1;i<=n+1;i++) a[i][tot+1]=b[i];
    Gauss();
    if(fabs(a[n+1][tot+1])<eps) puts("0.0");
    else printf("%.1lf\n",-a[n+1][tot+1]);
}
View Code

  老司机的狂欢

    主要在于题意转换

    将老司机的初始位置为x,按x从大到小排序,若t秒后老司机不相遇或追及,那么老司机的最终位置还是有序的,即LIS问题,用树状数组维护

    考虑随时间递增,不会相遇的老司机数量会单调递减,可以二分时间,找到k个老司机不相遇的时间t,如果最终二分出的时间算出来有多于k个老司机可以不相遇,那么老司机就要有意见了,输出t后puts("-1")

    如果恰好k个老司机的话,需要求出最小字典序的方案,看求LIS过程,是从前面的某一个位置小于我而序列长度最大的转移到我,类似与一棵树的样子找深度最大的权值小于我的点连边

    如果有两个点满足深度都是最大的,且权值(最终位置)都小于我的,那么求两个点的lca选取点到lca的路径上最小id最小的那个点连边

    因为考虑输出按id排序之后字典序最小,如果最小id最小,那么这条链必定比最小id大的字典序小

    最后找k深度的字典序最小的那条树上从叶子节点到根的链,按id排序后输出即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,k,st[110000],fa[110000][20],minn[110000][20],dis[110000];
struct node{
    long long x,a,pos;
    int id;
    bool operator < (const node b)const{
        return x<b.x;
    }
}w[110000];
long long tmp[110000];
pair<int,int>tr[110000];
char lca(int x,int y){
    int minnx=x,minny=y;
    int i=0;
    for(;(1<<i)<=dis[y];i++);
    for(register int j=i;j>=0;j--){
        if(fa[y][j]!=fa[x][j]){
            minnx=min(minnx,minn[x][j]);
            minny=min(minny,minn[y][j]);
            x=fa[x][j];
            y=fa[y][j];
        }
    }
    return minnx<minny;
}
pair<int,int>askmax(pair<int,int>a,pair<int,int>b){
    if(a.first<b.first) return b;
    else if(a.first>b.first) return a;
    if(lca(a.second,b.second)) return a;
    return b;
}
int lowbit(int x){return x&(-x);}
void add(int x,pair<int,int>w){
    while(x<=n+1){
        tr[x]=max(tr[x],w);
        x+=lowbit(x);
    }
}
pair<int,int> ask(int x){
    pair<int,int>ans=make_pair(0,0);
    while(x){
        ans=max(ans,tr[x]);
        x-=lowbit(x);
    }
    return ans;
}
int judge(long long mid){
    tmp[0]=0;
    for(register int i=1;i<=n;i++){
        w[i].pos=w[i].x+w[i].a*mid*mid/2;
        tmp[++tmp[0]]=w[i].pos;
    }
    sort(tmp+1,tmp+tmp[0]+1);
    tmp[0]=unique(tmp+1,tmp+tmp[0]+1)-tmp-1;
    for(register int i=1;i<=n;i++)
        w[i].pos=lower_bound(tmp+1,tmp+tmp[0]+1,w[i].pos)-tmp;
    for(register int i=1;i<=n+1;i++) tr[i]=make_pair(0,0);
    for(register int i=1;i<=n;i++){
        pair<int,int> cet=ask(w[i].pos);
        cet.first++;cet.second=w[i].id;
        add(w[i].pos+1,cet);    
    }
    pair<int,int>cet=ask(n+1);
    return cet.first;
}
void buildadd(int x,pair<int,int>w){
    while(x<=n+1){
        tr[x]=askmax(tr[x],w);
        x+=lowbit(x);
    }
}
pair<int,int> buildask(int x){
    pair<int,int>ans=make_pair(0,0);
    while(x){
        ans=askmax(ans,tr[x]);
        x-=lowbit(x);
    }
    return ans;
}
void build(){
    for(register int i=1;i<=n+1;i++) tr[i]=make_pair(0,0);
    memset(fa,0,sizeof(fa));
    memset(minn,0x3f,sizeof(minn));
    for(register int i=1;i<=n;i++){
        pair<int,int> cet=buildask(w[i].pos);
        fa[w[i].id][0]=cet.second;
        minn[w[i].id][0]=cet.second;
        dis[w[i].id]=dis[cet.second]+1;
        for(register int j=1;j<=16;j++){
            fa[w[i].id][j]=fa[fa[w[i].id][j-1]][j-1];
            minn[w[i].id][j]=min(minn[w[i].id][j-1],minn[fa[w[i].id][j-1]][j-1]);
        }
        cet.first++;cet.second=w[i].id;
        buildadd(w[i].pos+1,cet);    
    }
}
int main(){
    //freopen("driver3.in","r",stdin);
    //freopen("3.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(register int i=1;i<=n;i++) scanf("%lld%lld",&w[i].x,&w[i].a),w[i].id=i;
    sort(w+1,w+n+1);
    int l=0,r=86400,mid,ans;
    while(l<=r){
        mid=(l+r)>>1;
        if(judge(mid)>=k) ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
    if(judge(ans)>k) puts("-1");
    else{
        build();
        int x=buildask(n+1).second;
        st[++st[0]]=x;
        while(fa[x][0]!=0){
            x=fa[x][0];
            st[++st[0]]=x;
        }
        sort(st+1,st+st[0]+1);
        for(register int i=1;i<=st[0];i++) printf("%d\n",st[i]);
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/heoitys/p/11686681.html