• USACO 2020 January Contest, Gold T3 SpringBoards


    Bessie 在一个仅允许沿平行于坐标轴方向移动的二维方阵中。她从点 ((0,0)) 出发,想要到达 ((N,N)(1≤N≤10^9))。为了帮助她达到目的,在方阵中有 (P(1≤P≤10^5))个跳板。每个跳板都有其固定的位置 ((x_1,y_1)),如果 Bessie 使用它,会落到点 ((x_2,y_2))
    Bessie 是一个过程导向的奶牛,所以她仅允许她自己向上或向右行走,从不向左或向下。类似地,每个跳板也设置为不向左或向下。Bessie 需要行走的距离至少是多少?

    一开始我还以为是什么最短路问题,后来感觉建图过于毒瘤不太对头。。。。仔细一想不就是一个树状数组优化的DP嘛!

    先来考虑朴素状态转移,令(f[i])为只使用前(i)个跳板,且一定使用了第(i)个跳板,所省下的最大距离。
    (f[i]=max{f[j]+x2[i]-x1[i]+y2[i]-y1[i]},x2[j]<=x1[i],y2[j]<=y1[i])
    而由于我们选定了(i),可以把(f[i])(x2[i],y2[i])一起考虑。
    这个问题就被转化为了类似二维数点问题,然而二维树状数组铁定MLE,因此我们可以降维:
    将每个跳板两头点混合在一起成为数组(a[]),以(x)为第一关键字、(y)为第二关键字排序,顺序访问所有点,即实现了(x)的单调。那么就成了(y)轴上的询问。那么树状数组可以以(y)轴为下标,存储(f[i])
    遇到起点就更新(f[跳板号]),也就是树状数组下标(a[i].y)前缀的最大值+这个跳板能越过的距离。
    遇到终点就将(f[跳板号])加入树状数组,也就是(a[i].y)后更新最大值。

    需要注意的是坐标范围过大,需要先离散化。这里新建了一个存储(y)坐标的数组(b[]),之后再映射回(a[i].y)即可。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN=2e5+5; //起点终点一起处理,双倍大小
    
    int n,p,tot,ntot,ans;
    int b[MAXN],tree[MAXN],f[MAXN],dis[MAXN]; //分别是:纵坐标暂时存储、树状数组、DP数组、单个跳板省下的距离
    
    struct node{
    	int x,y,chk,id; // 坐标、顶点性质、跳板号
    	bool operator < (const node &A)const{
            if(x!=A.x)
    			return x<A.x;
            if(y!=A.y)
    			return y<A.y;
            return chk<A.chk;
        }
    }a[MAXN];
    
    void add(int k,int x) // 树状数组添加结点更新最大值
    {
    	for(;k<=tot;k+=(k&-k))
    		tree[k]=max(tree[k],x);
    }
    
    int ask(int k) // 树状数组查询前缀
    {
    	int ret=0;
    	for(;k;k-=(k&-k))
    		ret=max(ret,tree[k]);
    	return ret;
    }
     
    int main()
    {
    	freopen("boards.in","r",stdin);
    	freopen("boards.out","w",stdout);
    	cin>>n>>p;
    	for(int i=1;i<=p;i++)
    	{
    		int x,y;
    		cin>>x>>y;
    		a[++tot]=(node){x,y,0,i};
    		b[tot]=y;
    		dis[i]-=x+y;
    		cin>>x>>y;
    		a[++tot]=(node){x,y,1,i};
    		b[tot]=y;
    		dis[i]+=x+y; // 计算dis数组,为之后计算f[i]做准备
    	}
    	a[++tot]=(node){n,n,2,0};
    	sort(b+1,b+tot+1);
    	ntot=unique(b+1,b+tot+1)-b-1; // 离散化纵坐标
    	for(int i=1;i<=tot;i++)
    		a[i].y=lower_bound(b+1,b+ntot+1,a[i].y)-b;
    	sort(a+1,a+tot+1);
            for(int i=1;i<=tot;i++)
    	{
                    if(a[i].chk==2)
    		{
                	        for(int j=1;j<=p;j++)
    			        if(a[i].x>=a[j].x && a[i].y>=a[j].y)
    					ans=max(ans,f[j]);
                    printf("%d
    ",2*n-ans); //不用跳板是2n,所以答案是2n-ans
                    return 0;
            }
            if(!a[i].chk)
    			f[a[i].id]=ask(a[i].y)+dis[a[i].id]; // 起点就更新
            else
    			add(a[i].y,f[a[i].id]); // 终点就增加
        }
        return 0;
    }
    
  • 相关阅读:
    他山之石____集合框架__【List,Set,Map之间的区别】
    集合框架__【泛型】
    集合框架__【Set集合】【HashSet】【TreeSet】
    模式串匹配,KMP算法——HDU1686
    模式串匹配,KMP算法——HDU1711
    网络最大流——POJ
    网络最大流——HDU
    拓扑排序——CodeForces-645D
    二分图染色,二分图匹配——HDU
    二分图匹配,最小点覆盖——POJ
  • 原文地址:https://www.cnblogs.com/InedibleKonjac/p/13401339.html
Copyright © 2020-2023  润新知