ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题解 [APIO2009]会议中心

2022-06-25 13:35:17  阅读:140  来源: 互联网

标签:会议 ch APIO2009 Min int 题解 meet dp define


\(题目传送门\)

提供一种 \(O(n\log^2(n))\) 的解法。

第一问是经典的最大不交线段集问题,可以用 \(O(n\log(n))\) 的贪心做法和 \(O(n^2)\) 的 \(dp\) 做法解决,但显然贪心处理字典序非常棘手,这里考虑 \(O(n^2)\)的\(dp\) 。

对于一个会议,结束时间越早越好,因此将所有会议离散化后按照结束时间升序排序,这时原来 \(O(n)\) 枚举转移可以用一棵支持单点修改,区间查询的权值线段树维护结束时间为 \(x\) 的最小的\(dp\)值做到 \(O(\log(n))\) ,但结束时间可能会相同,因此在线段树上还要维护最小的字典序。一个显然的方法是以 \(dp\) 值为第一关键字,以会议编号为第二关键字取最优值,代码如下:

int n,tmp[N<<1],cnt;
bool operator<(const pair<int,int>&x,const pair<int,int>&y){
    return(x.first==y.first)?x.second<y.second:x.first>y.first;
}
namespace SGT{
    pair<int,int>Max[N<<2];
    void pushup(int k){
    	Max[k]=(Max[k<<1]<Max[k<<1|1])?Max[k<<1]:Max[k<<1|1];
    }
    void modify(int k,int l,int r,int x,pair<int,int>pr){
        if(l==r){
            if(pr<Max[k])Max[k]=pr;
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid)modify(k<<1,l,mid,x,pr);
        else modify(k<<1|1,mid+1,r,x,pr);
        pushup(k);
    }
    pair<int,int>query(int k,int l,int r,int x,int y){
		if(r<x||l>y)return make_pair(0,0);
    	if(l>=x&&r<=y)return Max[k];
        int mid=(l+r)>>1;
        if(y<=mid)return query(k<<1,l,mid,x,y);
        if(mid<x)return query(k<<1|1,mid+1,r,x,y);
        if(query(k<<1,l,mid,x,y)<query(k<<1|1,mid+1,r,x,y))return query(k<<1,l,mid,x,y);
        return query(k<<1|1,mid+1,r,x,y);
    }
}
using namespace SGT;
namespace Solve{
    struct Node{int l,r,id;}meet[N];
    int dp[N],pre[N],ans[N];
    void print(){
        int id=0,tot=0;
        for(int i=1;i<=n;i++)
            if(make_pair(dp[i],i)<make_pair(dp[id],id))id=i;
		printf("%d\n",dp[id]);
		while(id)ans[++tot]=id,id=pre[id];
        sort(ans+1,ans+tot+1);
        for(int i=1;i<=tot;i++)printf("%d ",ans[i]);
    }
    void DP(){
        sort(meet+1,meet+n+1,[&](Node&x,Node&y){return x.r<y.r;});
        for(int i=1;i<=n;i++){
            pair<int,int>u=query(1,1,cnt,1,meet[i].l-1);
            dp[meet[i].id]=dp[u.second]+1,pre[meet[i].id]=u.second;
            modify(1,1,cnt,meet[i].r,make_pair(dp[meet[i].id],meet[i].id));
        }
        print();
    }
}
using namespace Solve;
int main(){
    n=read();
    for(int i=1;i<=n;i++)meet[i].l=read(),meet[i].r=read(),meet[i].id=i;
    for(int i=1;i<=n;i++){
        tmp[++cnt]=meet[i].l;
        tmp[++cnt]=meet[i].r;
    }
    sort(tmp+1,tmp+cnt+1);
    cnt=unique(tmp+1,tmp+cnt+1)-tmp-1;
    for(int i=1;i<=n;i++){
        meet[i].l=lower_bound(tmp+1,tmp+cnt+1,meet[i].l)-tmp;
        meet[i].r=lower_bound(tmp+1,tmp+cnt+1,meet[i].r)-tmp;
    }
    DP();
    return 0;
}

但这样的做法有问题,考虑下面这组数据:

输入:

6
1 3
5 7
4 5
6 10
10 12
13 15

输出:

4
1 2 5 6

代码输出

4
1 3 4 6

究其原因在于当前策略在之前的字典序中不一定最优,那么这样的做法是错的吗?注意到 \(dp\) 值与其转移构成一棵森林,而相同 \(dp\) 值比较字典序时在所在树中的深度一定相等,想到树上倍增,具体地,每次 \(dp_u\) 转移后记 \(fa_{u,0}=pre_u\) , \(Min_{u,0}=u\) ,更新 \(u\) 的倍增数组,线段树内比较时在树上跳,跳到路径最小值相等时停止,树上到根路径上的最小值为字典序。

似乎把线段树去掉就是一个 \(\log\) ?没实现过

代码如下:

#include<bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
#define N 400005
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
#define il inline
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
il int read(){
    int w=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
    while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
    return w*h;
}
struct Node{
    int l,r,id;
    bool operator<(const Node&p)const{
        return r<p.r;
    }
}mt[N];
int n,tot,tmp[N];
int dp[N],from[N],ans[N];
namespace Jump{
    int fa[N][25],Min[N][25];
    void build(int u){
        for(int i=1;i<=21;i++){
            fa[u][i]=fa[fa[u][i-1]][i-1];
            Min[u][i]=min(Min[u][i-1],Min[fa[u][i-1]][i-1]);
        }
    }
    bool check(int u,int v){
        int minx=INF,miny=INF;
        for(int i=21;i>=0;i--)
            if(Min[u][i]!=Min[v][i]){
                minx=min(minx,Min[u][i]);
                miny=min(miny,Min[v][i]);
                u=fa[u][i];v=fa[v][i];
            }
        return minx>miny;
    }
}
bool operator<(const pii&x,const pii&y){
    return(x.fi==y.fi)?Jump::check(x.se,y.se):x.fi<y.fi;
}
namespace SGT{
    pii Max[N<<2];
    pii pushup(pii l,pii r){return(l<r)?r:l;}
    void modify(int k,int l,int r,int x,pii pa){
        if(l==r){
            if(Max[k]<pa)Max[k]=pa;
            return;
        }
        if(x<=mid)modify(ls,l,mid,x,pa);
        if(mid<x)modify(rs,mid+1,r,x,pa);
        Max[k]=pushup(Max[ls],Max[rs]);
    }
    pii query(int k,int l,int r,int x,int y){
        if(l>y||r<x)return mp(0,0);
        if(l>=x&&r<=y)return Max[k];
        if(y<=mid)return query(ls,l,mid,x,y);
        if(mid<x)return query(rs,mid+1,r,x,y);
        return pushup(query(ls,l,mid,x,y),query(rs,mid+1,r,x,y));
    }
}
void out(int u){
    if(u==0)return;
    out(from[u]);
    ans[++tot]=u;
}
void print(){
    int id=0;
    for(int i=1;i<=n;i++)
        if(mp(dp[id],id)<mp(dp[i],i))id=i;
    printf("%lld\n",dp[id]);
    tot=0;
    out(id);
    sort(ans+1,ans+tot+1);
    for(int i=1;i<=tot;i++)cout<<ans[i]<<' ';
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        mt[i].l=read();mt[i].r=read();mt[i].id=i;
        tmp[++tot]=mt[i].l;
        tmp[++tot]=mt[i].r;
    }
    sort(tmp+1,tmp+tot+1);
    tot=unique(tmp+1,tmp+tot+1)-tmp-1;
    for(int i=1;i<=n;i++){
        mt[i].l=lower_bound(tmp+1,tmp+tot+1,mt[i].l)-tmp;   
        mt[i].r=lower_bound(tmp+1,tmp+tot+1,mt[i].r)-tmp;
    }
    sort(mt+1,mt+n+1);
    for(int i=1;i<=n;i++){
        pii u=SGT::query(1,1,tot,1,mt[i].l-1);
        int id=mt[i].id;
        if(u.se==0)dp[id]=1,from[id]=0;
        else dp[id]=dp[u.se]+1,from[id]=u.se;
//      cout<<id<<' '<<dp[id]<<' '<<from[id]<<endl;
        Jump::fa[id][0]=(u.se)?u.se:id;
        Jump::Min[id][0]=id;
        Jump::build(id);
        SGT::modify(1,1,tot,mt[i].r,mp(dp[id],id));
    }
    print();
    return 0;
}

标签:会议,ch,APIO2009,Min,int,题解,meet,dp,define
来源: https://www.cnblogs.com/pidan123/p/15503117.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有