首页 > 技术文章 > 20191015+

remarkable 2019-10-16 17:31 原文

前言

  • 连第二机房房主也不是了,该高兴还是该呵呵?
  • 板子不会还是硬伤,不会高斯消元的我看到T2都绝望了,YY还写错了……
  • T3特判都打错,佛啦。
  • 咳咳。还有为什么还是这么颓废。
  • 还有%%%文化课巨佬FlashHu

以上纯属CD。

T1

  • 先把(a,b)按a单调下降排序,然后将不符合b单调上升的点删除,因为这些点不可能成为答案。
  • 然后将给出的(a,b)抽象成$(\frac{1}{a},\frac{1}{b})$的点,因为a单调下降b单调上升所以新点集的横坐标单调上升纵坐标单调递减。
  • 直接用栈维护左下凸包,栈内斜率单调递增即可。
  • 时间复杂度$\Theta(NlogN)$,空间复杂度$\Theta(N)$。
  • 本题精度稍GS,蒟蒻博主的垃圾代码开__float128才能AC……
#include<cstdio>
#include<algorithm>
#include<vector>
#define ll long long
#define db __float128
using namespace std;
typedef pair<pair<int,int>,int> pi;
int const N=3e5+5;
db const lar=2e16,eps=-1e-6;
int n,t;
pi b[N];
pair<int,int>a[N];
vector<int>bs[N];
int ans[N];
int stk[N],tp;
db xp[N],yp[N],xl[N];
inline int read(){
    int ss(0);char bb(getchar());
    while(bb<48||bb>57)bb=getchar();
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss;
}
inline db _max(db x,db y){
    return x>y?x:y;
}
inline db _min(db x,db y){
    return x<y?x:y;
}
int main(){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read();
    for(register int i=1;i<=n;++i)b[i].first.first=read(),b[i].first.second=read(),b[i].second=i;
    sort(b+1,b+n+1,[](pi skyh,pi yxs){
        return (skyh.first.first^yxs.first.first)?skyh.first.first>yxs.first.first:skyh.first.second>yxs.first.second;
    });
    for(register int i=1;i<=n;++i)
        if(b[i].first.second>a[t].second)a[++t]=b[i].first,bs[t].push_back(b[i].second);
        else if(b[i].first.first==a[t].first && b[i].first.second==a[t].second)bs[t].push_back(b[i].second);
    n=t,t=0;
    for(register int i=1;i<=n;++i)
        xp[i]=1.0/(db)a[i].first,yp[i]=1.0/(db)a[i].second;
    for(register int i=1;i<=n;++i){
        while(tp>1 && (yp[i]-yp[stk[tp]])/(xp[i]-xp[stk[tp]])<xl[tp])--tp;
        stk[++tp]=i,xl[tp]=(yp[i]-yp[stk[tp-1]])/(xp[i]-xp[stk[tp-1]]);
    }
    /*for(register int i=1;i<=n;++i){
        db minx=lar,maxx=0;
        const db xn=xp[i],yn=yp[i];
        for(register int j=1;j<i;++j)
            maxx=_max(maxx,(xn-xp[j])/(yp[j]-yn));
        for(register int j=i+1;j<=n;++j)
            minx=_min(minx,(xn-xp[j])/(yp[j]-yn));
        //printf("%.15lf %.15lf %d %d\n",maxx,minx,a[i].first,a[i].second);
        if(minx-maxx>=eps)
            for(auto&j:bs[i])ans[++t]=j;
    }*/
    for(register int i=1;i<=tp;++i)
        for(auto &j:bs[stk[i]])ans[++t]=j;
    sort(ans+1,ans+t+1);
    for(register int i=1;i<=t;++i)printf("%d ",ans[i]);
    puts("");
    return 0;
}
View Code

T2

  • 体验到了板子不会打的绝望。
  • 裸的高斯消元。
  • 不过会高斯消元也得不了多少分因为自己不会得到一个方程的解。
  • 正解直接将目标方程的元消去,最终得到的解就是答案的相反数,因为保证有解所以一定可以消干净。
  • 时间复杂度$\Theta(N^3)$,空间复杂度$\Theta(N^2)$。
  • 听说不少人觉得读入难,其实吧,只要用一个map映射string就可以了,挺简单的,考试5分钟就打完了。
#include<cstdio>
#include<string>
#include<map>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int const N=203;
int n,tot;
double a[N][N],c[N];
map<string,int>qj;
bool vis[N];
inline double _abs(double x){
    return x<0?-x:x;
}
inline void _swap(double &x,double &y){
    double z=x;
    x=y,y=z;
    return ;
}
inline void Gauss(){
    for(register int i=1,now=1;i<n;now=++i){
        for(register int j=i+1;j<n;++j)
            if(_abs(a[j][i])>_abs(a[now][i]))now=j;
        if(!_abs(a[now][i]))continue;
        double x=a[now][i];
        for(register int j=1;j<=n;++j)
            _swap(a[i][j],a[now][j]),a[i][j]/=x;
        for(register int j=1;j<=n;++j){
            if(j==i)continue;
            x=a[j][i];
            for(register int k=i;k<=n;++k)
                a[j][k]-=a[i][k]*x;
        }
    }
    return ;
}
int main(){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    scanf("%d",&n);
    string s;
    char ss[5];double zz;
    int xs=1;
    for(register int i=1;i<=n;++i,xs=1)
        while(1){
            scanf("%lf",&zz);zz*=xs;
            cin>>s;
            if(qj[s])a[i][qj[s]]=zz;
            else a[i][qj[s]=++tot]=zz;
            scanf("%s",ss);
            if(ss[0]=='=')xs=-1;
            else if(ss[0]=='H'){
                scanf("%lf",&c[i]);
                break;
            }
        }
    n=(tot>n?tot:n)+1;
    for(register int i=1;i<n;++i)a[i][n]=c[i];
    while(1){
        scanf("%lf",&zz),zz*=xs;
        cin>>s;
        if(qj[s])a[n][qj[s]]=zz;
        else a[n][qj[s]=++tot]=zz;
        scanf("%s",ss);
        if(ss[0]=='=')xs=-1;
        else if(ss[0]=='H')break;
    }
    n=max(n,tot+1);
    Gauss();
    a[n][n]?printf("%.1lf",-a[n][n]):puts("0.0");
    return 0;
}
View Code

T3

  • dfs+信仰-1成功30。
  • 首先时间显然随k的增加单调不降,所以可以二分答案。
  • 因为选出的k个人的位置在t时刻后的相对位置不能发生改变,所以题意可以转化成最长上升子序列。
  • 求出时间后,我们考虑如何构造字典序最小的最优解。
  • LIS可以看成一棵树,每次转移相当于新加一个叶节点。
  • 对于两个深度都为k的节点x,y,设lca(x,y)=fa。
  • 那么如果x到fa路径上的最小编号小于y到fa路径上的最小编号,那么深度为k的解x比y优。
  • 于是我们可以求出任意深度的最优解,但这个最优解的转移位置任意,而LIS的解必须保证权值单调上升。
  • 我们可以对于每个深度开一棵动态开点线段树,维护前缀最优解就可以正确转移了。
  • 由于博主太垃圾不会树状数组所以无论是LIS还是构造字典序最小的解都用的线段树而且不是zkw,所以常数巨大会T。
  • 所以博主用了一个优化lca的常用方法。
  • 可能大家不是很在意倍增lca的log,但事实上直接设一个极值比如log100000的做法常数很大。
  • 优化很简单,预处理出1~n每个数的log值,进行倍增时只倍增到log[dep]。进行这种优化后博主可以通过本题。
  • 时间复杂度$\Theta(NlogNlogT+Nlog^2N)$,空间复杂度$\Theta(NlogN)$。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define L tr[k].lc
#define R tr[k].rc
#define LC Tr[k].ls
#define RC Tr[k].rs
using namespace std;
int const N=1e5+5,M=86400;
int n,k;
struct node{
    int a,id;
    ll c;
}q[N];
int p[N];
pair<long double,int>b[N];
struct Ljj{
    int lc,rc,w;
}tr[N<<3];
struct S_Tree{
    int ls,rs,val,f;
}Tr[N*100];
int tot,rt[N],Log[N],dep[N],f[N][18],mx[N][18];
int ans[N];
inline ll read(){
    ll ss(0),pp(1);char bb(getchar());
    for(;bb<48||bb>57;bb=getchar())if(bb=='-')pp=-1;
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss*pp;
}
inline void _swap(int &x,int &y){
    int z=x;
    x=y,y=z;
    return ;
}
inline int min(int x,int y){
    return x<y?x:y;
}
inline int max(int x,int y){
    return x>y?x:y;
}
void build(int x,int y,int k){
    L=x,R=y;
    if(x==y)return ;
    int mid=x+y>>1;
    return build(x,mid,k<<1),build(mid+1,y,k<<1|1);
}
void clear(int k){
    if(!L||!tr[k].w)return ;
    tr[k].w=0;
    return clear(k<<1),clear(k<<1|1);
}
int ask(int x,int y,int k){
    if(L>=x&&R<=y)return tr[k].w;
    int mid=L+R>>1,as=0;
    if(x<=mid)as=ask(x,y,k<<1);
    if(y>mid)as=max(as,ask(x,y,k<<1|1));
    return as;
}
void add(int x,int y,int k){
    if(L==R){tr[k].w=max(tr[k].w,y);return ;}
    int mid=L+R>>1;
    if(x<=mid)add(x,y,k<<1);
    else add(x,y,k<<1|1);
    tr[k].w=max(tr[k<<1].w,tr[k<<1|1].w);
    return ;
}
inline bool check(int x){
    clear(1);
    for(register int i=1;i<=n;++i)b[i].first=0.5*x*x*q[i].a+q[i].c,b[i].second=i;
    sort(b+1,b+n+1);
    int now=0;
    b[0].first=b[1].first+1;
    for(register int i=1;i<=n;++i)
        p[b[i].second]=now=now+(b[i].first!=b[i-1].first);
    for(register int i=1,z;i<=n;++i){
        z=0;
        if(p[i]!=1)z=ask(1,p[i]-1,1);
        add(p[i],++z,1);
        if(z>=k)return 1;
    }
    return 0;
}
inline int getmin(int x,int y){
    if(!x||!y)return x+y;
    if(x==y)return x;
    int xm=x,ym=y,xx=x,yy=y;
    for(register int i=Log[dep[x]];~i;--i)
        if(f[x][i]^f[y][i]){
            xm=min(xm,mx[x][i]),ym=min(ym,mx[y][i]);
            x=f[x][i],y=f[y][i];
        }
    return xm<ym?xx:yy;
}
inline void down(int k){
    if(!LC)LC=++tot;
    if(!RC)RC=++tot;
    int x=Tr[k].f;
    Tr[k].f=0;
    Tr[LC].val=getmin(Tr[LC].val,x),Tr[LC].f=getmin(Tr[LC].f,x);
    Tr[RC].val=getmin(Tr[RC].val,x),Tr[RC].f=getmin(Tr[RC].f,x);
    return ;
}
int Query(int l,int r,int x,int k){
    if(!k)return 0;
    if(r<=x)return Tr[k].val;
    if(Tr[k].f&&l^r)down(k);
    int mid=l+r>>1,zz=Query(l,mid,x,LC);
    if(x>mid)zz=getmin(zz,Query(mid+1,r,x,RC));
    return zz;
}
void Insert(int l,int r,int x,int y,int &k){
    if(!k)k=++tot;
    if(l>=x){
        Tr[k].val=getmin(y,Tr[k].val),Tr[k].f=getmin(y,Tr[k].f);
        return ;
    }
    if(Tr[k].f&&l^r)down(k);
    int mid=l+r>>1;
    if(x<=mid)Insert(l,mid,x,y,LC);
    Insert(mid+1,r,x,y,RC);
    Tr[k].val=getmin(Tr[LC].val,Tr[RC].val);
    return ;
}
int main(){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read(),k=read();
    Log[0]=-1;
    for(register int i=1;i<=n;++i)q[i].c=read(),q[i].a=read(),q[i].id=i,Log[i]=Log[i>>1]+1;
    sort(q+1,q+n+1,[](node skyh,node yxs){
        return skyh.c<yxs.c;
    });
    build(1,n,1);
    int l=0,r=M;
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid))l=mid;
        else r=mid-1;
    }
    printf("%d\n",l);
    ++k;
    if(check(l))return puts("-1"),0;
    clear(1),memset(mx,0x3f,sizeof(mx));
    for(register int i=1;i<=n;++i)b[i].first=0.5*l*l*q[i].a+q[i].c,b[i].second=i;
    sort(b+1,b+n+1);
    int now=0;
    b[0].first=b[1].first+1;
    for(register int i=1;i<=n;++i)
        p[b[i].second]=now=now+(b[i].first!=b[i-1].first);
    for(register int i=1,z,x,y;i<=n;++i){
        z=0,y=q[i].id;
        if(p[i]!=1)z=ask(1,p[i]-1,1);
        add(p[i],z+1,1);
        if(p[i]!=1)f[y][0]=Query(1,n,p[i]-1,rt[z]);
        mx[y][0]=min(y,f[y][0]),dep[y]=z+1;
        for(register int j=0;j<Log[z];++j)
            f[y][j+1]=f[f[y][j]][j],mx[y][j+1]=min(mx[y][j],mx[f[y][j]][j]);
        Insert(1,n,p[i],y,rt[z+1]);
    }
    now=Query(1,n,n,rt[k-1]);
    for(register int i=1;i<k;++i)
        ans[i]=now,now=f[now][0];
    sort(ans+1,ans+k);
    for(register int i=1;i<k;++i)
        printf("%d\n",ans[i]);
    return 0;
}
View Code

推荐阅读