[USACO19FEB]Mowing Mischief

题目大意:

给定平面上的一些点,求这些点的一个\(LIS\),并且还需要满足下列式子最小:
\[ \sum_{i=1}^{n-1}(a[i+1].x-a[i].x)*(a[i+1].y-a[i].y) \]

题解:

比较巧妙的一道题。

首先我们需要找出一个性质,我们先令\(dp[i]\)表示以\(i\)点结尾的\(LIS\),然后这些\(LIS\)相同的点在平面上是横坐标递增,纵坐标递减的,下面我们说的转移点的顺序都是按照这个顺序来的。

然后我们在观察转移,我们令两个转移点\(j\)\(k\),若\(k\)\(j\)更优,那么有:
\[ dp[k]+(a[i].x-a[k].x)*(a[i].y-a[k].y)\geq dp[j]+(a[i].x-a[j].x)*(a[i].y-a[j].y) \]

\[ a[i].x*(a[j].y-a[k].y)+a[i].y*(a[j].x-a[k].x)\geq dp[j]-dp[k]+a[j].x*a[j].y-a[k].x*a[k].y \]

\[ A*a[i].x+B*a[i].y\geq C \]

可以看出,这其实是一个半平面,结合上面的性质,对于一排待转移点,更优的转移是一段前缀或者一段后缀,这启发我们这道题中有决策单调性。

但是这个东西还有一个条件就是\(a[i].x\geq a[j].x\ \ a[i].y\geq a[j].y\),这个东西其实我们发现合法的转移点也是一段连续的区间,这启发我们在外面线段树分治解决这个限制。

对于决策单调性的部分,我们可以令\(k\)\(j\)的后面一个点,那么上面的\(A\)是负的\(B\)是正的,所以合法的区域在直线上方,按照这个做决策单调性就好了。

代码
#include<bits/stdc++.h>
#define M 1000009
#define N 200009
using namespace std;
typedef long long ll;
vector<int>vec[N],now;
vector<int>::iterator it;
int n,T,dp[N];
ll f[N],ans;
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct BIT{
    int tr[M];
    inline void add(int x,int y){
        while(x<=T)tr[x]=max(tr[x],y),x+=x&-x;
    }
    inline int query(int x){
        int ans=0;
        while(x)ans=max(ans,tr[x]),x-=x&-x;
        return ans;
    }
}T1;
struct point{
    int x,y;
    inline bool operator <(const point &b)const{
        if(x!=b.x)return x<b.x;
        else return y<b.y;
    }
}a[N];
struct seg{
    #define ls tr[cnt].l
    #define rs tr[cnt].r
    int rot,tott;
    struct node{
        int l,r;
        vector<int>nw;
    }tr[N<<1];
    void build(int &cnt,int l,int r){
        cnt=++tott;tr[cnt].l=tr[cnt].r=0;
        tr[cnt].nw.clear();
        if(l==r)return;
        int mid=(l+r)>>1; 
        build(ls,l,mid);build(rs,mid+1,r);
    }
    inline void init(){
        tott=0;
        build(rot,0,now.size()-1);
    }
    inline void upd(int cnt,int l,int r,int id){
        if(a[id].x>=a[now[r]].x&&a[id].y>=a[now[l]].y){
            tr[cnt].nw.push_back(id);
            return;
        }
        if(a[id].x<a[now[l]].x||a[id].y<a[now[r]].y)return;
        if(l==r)return;
        int mid=(l+r)>>1;
        upd(ls,l,mid,id);upd(rs,mid+1,r,id);
    }
    inline void _upd(int tag,int l,int r,int L,int R){
        if(l>r)return; 
        int no=tr[tag].nw[(l+r)>>1];
        ll biu=1e18,tg=0;
        for(int i=L;i<=R;++i){
            int id=now[i];
            ll x=f[id]+1ll*(a[no].x-a[id].x)*(a[no].y-a[id].y);
            if(x<biu){
                biu=x;
                tg=i;
            }
        }
        f[no]=min(f[no],biu);
        int mid=(l+r)>>1;
        _upd(tag,l,mid-1,tg,R);
        _upd(tag,mid+1,r,L,tg);
    } 
    inline void work(int id){
        upd(rot,0,now.size()-1,id);
    }
    void solve(int cnt,int l,int r){
        _upd(cnt,0,tr[cnt].nw.size()-1,l,r);
        if(l==r)return;
        int mid=(l+r)>>1;
        solve(ls,l,mid);solve(rs,mid+1,r);
    }
    inline void solve(){
        solve(rot,0,now.size()-1);
    }
    #undef ls
    #undef rs
}T2;
int main(){
    n=rd();T=rd();
    for(int i=1;i<=n;++i){
        a[i].x=rd();a[i].y=rd();
    }
    sort(a+1,a+n+1);
    int maxx=0;
    for(int i=1;i<=n;++i){
        dp[i]=T1.query(a[i].y)+1;
        T1.add(a[i].y,dp[i]);
        vec[dp[i]].push_back(i);
        maxx=max(maxx,dp[i]);
    }
    memset(f,0x3f,sizeof(f));
    for(it=vec[1].begin();it!=vec[1].end();++it){
        int x=*it;
        f[x]=1ll*a[x].x*a[x].y;
    }
    for(int i=2;i<=maxx;++i){
        now=vec[i-1];
        T2.init();
        for(it=vec[i].begin();it!=vec[i].end();++it){
            int x=*it;
            T2.work(x);
        }
        T2.solve();
    }
    ans=1e18;
    for(it=vec[maxx].begin();it!=vec[maxx].end();++it){
        int x=*it;
        ans=min(ans,f[x]+1ll*(T-a[x].x)*(T-a[x].y));
    }
    cout<<ans; 
    return 0;
}

转载于:https://www.cnblogs.com/ZH-comld/p/10788443.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 题目描述 有N头奶牛,它们在M个牛棚之间相互转移。每个牛棚里有一些奶牛,每分钟可以容纳一头奶牛。一头奶牛从一个牛棚走到另一个牛棚需要一分钟的时间。现在,这些奶牛要开一个牛派对,它们要在同一时间到达同一个牛棚,所以它们需要在某个牛棚等待一段时间。你需要计算最小的等待时间,使得所有奶牛都能够在同一时间到达同一个牛棚。 输入格式 第一行包含三个整数N,M,X。 接下来M行,每行包含三个整数a,b,t,表示牛棚a和牛棚b之间有一条双向边,需要t分钟才能通过。 输出格式 输出一个整数,表示最小等待时间。 数据范围 1≤N≤500 1≤M≤10000 1≤X≤N 1≤a,b≤N 1≤t≤1000 输入样例#1 3 3 1 1 2 5 2 3 5 1 3 10 输出样例#1 5 输入样例#2 4 5 4 1 2 10 2 3 10 3 4 10 4 1 10 1 3 20 输出样例#2 30 算法1 (最短路) $O(N^3)$ Dijkstra算法 Dijkstra(迪杰斯特拉)算法是由荷兰计算机科学家狄克斯特拉于1956年发明的,因此又叫狄克斯特拉算法。 Dijkstra算法是一种贪心算法,用于求解一个节点到其他所有节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。 具体做法是:设立一个数组dis来保存源点到各个顶点的最短距离和一个数组book[i]来记录一个顶点是否已经在队列中。 初始时,原点s的路径权重被赋为0 (dis[s] = 0)。若对于顶点s存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,所有顶点并不属于任何已知最短路径所包含的顶点集合,因此都被标记为未知最短路径长度。当算法结束时,dis[v]中存储的便是源点s到顶点v的最短路径,或者如果从s无法到达v,则值为INF。 Dijkstra算法流程: 算法流程: 1. 将所有顶点分为两部分:已知最短路的顶点集合P和未知最短路的顶点集合Q。 2. 初始时,顶点集合P中只有源点s一个元素,以源点s为起点向外扩展。 3. 每次从顶点集合Q中选取一个顶点u(u的dist最小),并加入到顶点集合P中,同时以u为中心进行扩展。 4. 重复步骤3,直到顶点集合Q为空或者终点被加入到顶点集合P中。 5. 算法结束,最短路径保存在dis数组中。 时间复杂度 Dijkstra算法的时间复杂度为O(N^2)。由于N较小,因此可以通过本题。 参考文献 Dijkstra算法讲解 C++ 代码 算法2 (最短路) $O(N^2)$ Floyd算法 Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。 Floyd算法的基本思想 设G=(V,E)是一个带权有向图,其邻接矩阵为W。V={v1,v2,……,vn},W[1:n,1:n],则该图的Floyd算法可描述如下: 时间复杂度 Floyd算法的时间复杂度为O(N^3)。由于N较小,因此可以通过本题。 参考文献 Floyd算法讲解 C++ 代码 算法3 (最短路) $O(N^2)$ Bellman-Ford算法 Bellman-Ford算法是一种单源最短路径算法,可以处理负权边,但不能处理负权回路。 Bellman-Ford算法的基本思想 对于图中的任意一条边(u, v),Bellman-Ford算法会对每一条边进行一次松弛操作(Relax),并且这些操作是按照顺序进行的:当算法进行第i次松弛操作时,它只会改变长度为i+1的路径上的顶点的值。因此,当算法执行完第n-1次松弛操作后,路径长度最长不超过n-1,此时所有最短路径都已经求出。 时间复杂度 Bellman-Ford算法的时间复杂度为O(N*M)。由于N和M的范围较小,因此可以通过本题。 参考文献 Bellman-Ford算法讲解 C++ 代码 ### 回答2: Usaco 2007 Feb的问题是关于Cow Party的。这个问题中,农夫约翰有N头奶牛,它们之间通过一些路径相互连接,并且每个路径都有一个长度。约翰想要在某个时间将它的所有奶牛聚集在一起举办一个派对,现在他想知道所有奶牛从各自的位置到达聚会地点所需的最短时间。 为了解决这个问题,我们可以使用Dijkstra算法。我们首先需要创建一个节点集合,包含所有的奶牛和派对地点,并且初始化每个节点的最短时间为无穷大。接下来,我们选取一个起点节点--聚会地点,并将它的最短时间设置为0。然后我们开始遍历所有的节点,每次选择一个最短时间未确定的节点,并更新它的邻居节点的最短时间。我们重复这个过程,直到所有节点的最短时间都确定。 在更新节点的最短时间时,我们需要根据节点之间的路径长度来更新。我们检查从当前节点到邻居节点的路径长度加上当前节点的最短时间是否小于邻居节点目前的最短时间。如果是,则更新邻居节点的最短时间为新的最短时间。 最后,我们可以得到所有奶牛到达聚会地点所需的最短时间。我们找到所有奶牛起始位置的最长最短时间,即为我们的答案。 通过使用Dijkstra算法,我们可以解决这个问题并得到最优解。因此,Usaco 2007 Feb的Cow Party问题可以通过这种方法解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值