• [3/12福建四校联考]


    来自FallDream的博客。未经允许,请勿转载,谢谢

    ------------------------------------------------

    A.跳马

    T组数据,每次给定x和y,要求从(0,0)走马字形(一个坐标变化1,另一个变化2)到(x,y)的最小步数。 T<=1000;x,y<=10^9

    先贪心,然后小暴力。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    const int d[8][2]={{2,-1},{2,1},{1,2},{1,-2},{-1,2},{-1,-2},{-2,1},{-2,-1}};
    int dis[105][105];
    int x,y,ans;
    int qx[20005],qy[20005],top,tail;
    
    inline int abs(int x){return x<0?-x:x;}
    
    void bfs()
    {
        memset(dis,0,sizeof(dis));dis[50][50]=1;top=tail=0;qx[++top]=50;qy[top]=50;
        while(top!=tail)
        {
            int xx=qx[++tail],yy=qy[tail];
            for(int i=0;i<8;i++)
            {
                int nx=xx+d[i][0],ny=yy+d[i][1];
                if(nx<0||ny<0||dis[nx][ny]||nx>100||ny>100) continue;
                dis[nx][ny]=dis[xx][yy]+1;qx[++top]=nx;qy[top]=ny;
            }     
        }
    }
    
    int main()
    {
        freopen("jump.in","r",stdin);
        freopen("jump.out","w",stdout);
        int t=read();
        while(t--)
        {
            x=abs(read());y=abs(read());    bfs();
            if(x<y)swap(x,y);ans=0;
            if(x>50&&x>2*y+3){int t=(x-2*y-50)/4;if(t>0){x-=t*4;ans+=t*2;}}
            if(x<y)swap(x,y);
                if(x>50) {int t=(x-y-100)/2;if(t>0){ans+=t*2;x-=t*4;y-=t*2;}}
                if(x>1000&&y>1000){int t=(x-1000)/3;x-=t*3;y-=t*3;ans+=t*2;}
            while(x+y>=50)
            {
                if(x<y)swap(x,y);
                if(x-4>=2*y)x-=4;else x-=4,y-=2;ans+=2;
            }
            printf("%d
    ",ans+dis[x+50][y+50]-1);
        }
        return 0;
    }

    B.绝对值

    给定n个实数,每个实数在li,ri中随机选取,求和的绝对值的期望值。abs(li,ri)<=10^6,n<=15

    积分?没听说过,贴个题解跑路。

    C.序列。

    有n个序列对,每个对有a和b两个值。你要把这个序列分成任意段,满足每一段的a的最大值的和不超过m,并且对于任意的i<j且bi<=aj,i和j要分在一段中。在这基础上,你要使得每一段的b的和最大值最小。

    求这个最小的最大值。n<=100000,m<=10^12,a,b<=10^9

    做法:先缩点。

    很容易想到二分一个答案,我们可以用f[i]表示前i个点满足条件时的a的最大值的和的最小值。用这样n^2的dp来check,复杂度n^2logn可以通过20分。

    然后我们发现在这个dp中,如果在可以选择的区间里,我们发现每个点,它到现在这个点的转移费用是中间的最大值,所以可以维护一个单调队列,使得其中的ai单调下降(很显然上升的话小的那个点并不可能成为转移费用)。这样对于每个ai,由于f是单调上升的,所以可以把它可以作为转移费用的这段区间的最左端的f值加上它的i值作为一个可能最优的答案扔到线段树里面,这样我们就把转移的复杂度降到了logn,可以通过啦。

    复杂度nlog^2n

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    #define N 262144
    #define INF 20000000000000000LL
    using namespace std;
    inline ll read()
    {
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,cnt=0;ll m;
    ll l[200005];
    ll a[100005],b[100005];
    ll f[100005];
    ll s[100005],mx[100005];
    ll T[N*2+5];
    ll q[100005],top,tail;
    int d[100005],lt[100005];
    
    void renew(int x,ll ad)
    {
        x+=N;T[x]=min(T[x],ad);
        for(x>>=1;x;x>>=1)T[x]=min(T[x<<1],T[x<<1|1]);
    }
    
    void change(int x,ll ad)
    {
        //cout<<"change"<<x<<" "<<ad<<endl;
        x+=N;T[x]=ad;
        for(x>>=1;x;x>>=1)T[x]=min(T[x<<1],T[x<<1|1]);
    }
    
    ll query(int l,int r)
    {
        l=max(l,1);ll sum=INF;
        for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
        {
            if(~l&1)sum=min(sum,T[l+1]);
            if( r&1)sum=min(sum,T[r-1]);    
        }
        return sum==INF?0:sum;
    }
    
    int get(int x,ll lim)
    {
        int l=1,r=x,mid,ans=x;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(s[x]-s[mid-1]>lim)l=mid+1;
            else ans=mid,r=mid-1;    
        }
        return ans-1;
    }
    
    bool check(ll x)
    {
        tail=top=1;q[tail]=1;for(int i=1;i<=N*2+1;i++)T[i]=INF;
        change(1,mx[1]);f[1]=mx[1];//cout<<x<<endl;
        for(int i=2;i<=n;i++)
        {        
            int pos=get(i,x);
            while(pos>=q[tail]&&top>=tail) ++tail;    
            while(mx[i]>=mx[q[top]]&&top>=tail)--top;
            q[++top]=i;change(top,f[(top==tail)?pos:q[top-1]]+mx[i]);
            change(tail,f[pos]+mx[q[tail]]);
            f[i]=query(tail,top);
        }    
    //    cout<<x<<" "<<f[n]<<endl;
        return f[n]<=m;
    }
    
    int main()
    {
        freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);
        n=read();m=read();
        for(int i=1;i<=n;i++)
        {
            l[i<<1]=a[i]=read();
            l[(i<<1)-1]=b[i]=read();
        }sort(l+1,l+2*n+1);int j=0;
        for(int i=1;i<=n<<1;i++) if(l[i]!=l[i-1])l[++j]=l[i];
        for(int i=1;i<=N*2+1;i++)T[i]=INF;
        for(int i=1;i<=n;i++)
        {
            int x=lower_bound(l+1,l+j+1,a[i])-l;
            int y=lower_bound(l+1,l+j+1,b[i])-l;
            lt[i]=query(1,x);if(!lt[i]) lt[i]=i;
            renew(y,i);
        }
        for(int i=1;i<=N*2+1;i++)T[i]=INF;
        for(int i=1;i<=n;i++)
            renew(i,lt[i]);    
        for(int i=n;i;)
        {
            int x=lt[i],y=query(x,i);
            for(;y<x;x=y,y=query(x,i));
            ++cnt;for(int j=x;j<=i;j++)mx[cnt]=max(mx[cnt],a[j]),s[cnt]+=b[j];
            i=x-1;
        }n=cnt; 
        ll l=1,r=2000000000000000LL,mid,ans;
        for(int i=1;i<=n;i++)swap(s[i],s[n-i+1]),swap(mx[i],mx[n-i+1]);
        for(int i=1;i<=n;i++)l=max(l,s[i]),s[i]+=s[i-1];
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid))ans=mid,r=mid-1;
            else l=mid+1;    
        }
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    关于扩展欧几里得算法___基础,基础中的基础
    bzoj 2152聪聪可可
    poj1741 树上的点分治
    POJ1201 区间
    codevs 2756树上的路径
    zoj1260 king
    栈与队列应用:迷宫问题(DFS非最短路径)
    估值为一亿的AI核心代码
    栈与队列应用:计算前缀表达式的值
    栈与队列:循环队列算法+可执行代码
  • 原文地址:https://www.cnblogs.com/FallDream/p/liankao312.html
Copyright © 2020-2023  润新知