zoukankan      html  css  js  c++  java
  • LG3626 [APIO2009]会议中心(倍增+树状数组)

    LG3626 [APIO2009]会议中心

    前言

    倍增的好题。不得不说,APIO 的题质量很高。

    解法

    为了方便计算,我们可以进行对每个节点离散化,最多会有 (2n) 个节点。

    首先假设我们已经求得了最大值。我们需要来构造一组方案。

    首先可以确定,我们要从第一条线段开始考虑,因为这样字典序一定最小。

    我们令 (g_{i,j})从点 (i) 开始,到 (j) 为止最多有多少线段。(注意,不一定要以 (i) 为起点,也不一定以 (j) 为终点)

    如果考虑一个待选区间 ([l,r]) 是否可以选择。如果现在把被选掉的点填上,对于目前最大的满足 (i<l,r<j),且 ([i,j]) 中没有一个日期被安排掉的 ([i,j]),若满足 (g_{i,l-1}+g_{r+1,j}+1=g_{i,j}),则这个区间可以被加入答案。读者自证不难

    这样既保证了答案最大,也保证了这条线段可以被加入。确定 ([i,j]) 可以用树状数组来实现,看代码就可以理解了。不放心还可以用线段树来做,思路会很清晰,只是代码烦了亿些。

    问题是,我们如何快速求出 (g(i,j)) 呢?

    可以考虑倍增。

    我们令 (f_{i,j})从点 (i) 开始往后跳 (2^j) 条线段所能到达的最小的右端点的值。同样,起点不一定是 (i)

    转移方程很简单(为了表示清楚,就用数组的形式了,下标表示有点乱):

    [f[i][j]=f[f[i][j-1]+1][j-1] ]

    初值是一个难点,我们可以先用每个区间更新一波。我们再做一遍 (f_{i,0}=min(f_{i,0},f_{i+1,0}))。想到这个不容易,理解很简单。

    需要铲平一个误区,对于不是区间起点的点,也需要做上述倍增,这样方便其它点的转移与维护。

    我们可以在 (O(log n)) 时间内求出 (g_{i,j}) 了。具体怎么做,看代码你就秒懂了。

    代码

    如果你想写线段树,那我祝你好运了。

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    #include<bits/stdc++.h>
    #define int long long
    #define lowbit(x) x&(-x)
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=4e5+10; 
    
    int n,m,cnt;
    int number[MAXN],f[MAXN][23],ans[MAXN];
    struct line {
    	int l,r;
    }e[MAXN];
    
    int uq(int x) {
    	return lower_bound(number+1,number+m+1,x)-number;
        //离散化
    }
    
    int findmax(int l,int r) {
    	int ans=0;
    	for(int i=20;i>=0;i--) {
    		if(f[l][i]<=r) {
    			ans+=(1<<i);
    			l=f[l][i]+1;
    		}
    	}
    	return ans;
    }
    
    struct bitmax {
    	int c[MAXN];
    	void add(int x,int val) {
    		for(;x<=m;x+=lowbit(x)) {
    			c[x]=max(c[x],val);
    		}
    	}
    	int query(int x) {
    		int ret=0;
    		for(;x;x-=lowbit(x)) {
    			ret=max(ret,c[x]);
    		}
    		return ret;
    	}
    }lm,rm;
    struct bitsum {
    	int c[MAXN];
    	void add(int x) {
    		for(;x<=m;x+=lowbit(x)) {
    			c[x]++;
    		}
    	}
    	int query(int x) {
    		int ret=0;
    		for(;x;x-=lowbit(x)) {
    			ret+=c[x];
    		}
    		return ret;
    	}
    }ls,rs;
    
    signed main() {
    	cin>>n;
    	for(int i=1;i<=n;i++) {
    		e[i].l=read();
    		e[i].r=read();
    		number[++m]=e[i].l;
    		number[++m]=e[i].r;
    	}
    	
    	sort(number+1,number+m+1);
    	m=unique(number+1,number+m+1)-number-1;
    	
    	for(int i=1;i<=m+1;i++) {
    		f[i][0]=m+1;
    	}
    	for(int i=1;i<=n;i++) {
    		e[i].l=uq(e[i].l);
    		e[i].r=uq(e[i].r);
    		f[e[i].l][0]=min(f[e[i].l][0],e[i].r);
    	} 
    	for(int i=m;i;i--) {
    		f[i][0]=min(f[i][0],f[1+i][0]);
    	}
    	
    	for(int j=1;j<=20;j++) {
    		for(int i=1;i<=m+1;i++) {
    			if(f[i][j-1]<=m)
    				f[i][j]=f[f[i][j-1]+1][j-1];
    			else {
    				f[i][j]=m+1;
    			}
    		}
    	}
    	
    	for(int i=1;i<=n;i++) {
    		if(cnt-rs.query(e[i].l-1)-ls.query(m-e[i].r)>0) {
    			continue;
    		}
            //此时这个区间已经被覆盖了,无需考虑了。
    		
    		int l=rm.query(e[i].l-1);
    		int r=m-lm.query(m-e[i].r);
    		if(findmax(l,e[i].l-1)+findmax(e[i].r+1,r)==findmax(l,r)-1) {
    			ans[++cnt]=i;
    			rs.add(e[i].r);
    			ls.add(m-e[i].l);
    			rm.add(e[i].r,e[i].r);
    			lm.add(m-e[i].l,m-e[i].l);
    		}
    	}
    	
    	cout<<cnt<<endl;
    	for(int i=1;i<=cnt;i++) {
    		printf("%lld ",ans[i]);
    	}
    	puts("");
    	return 0;
    }
    /*
    4
    1 4
    2 3
    4 5
    10 12
    */
    
  • 相关阅读:
    mongodb性能测试:long时间戳与string格式时间
    .netcore mongodb 分页+模糊查询+多条件查询
    .netcore 图片处理
    ELEMENT-UI 封装el-table 局部刷新row
    vue-upload 封装组件-上传组件
    vue实现v-model父子组件间的双向通信
    cc.AudioSource
    Chrome插件:本地程序实现验证码破解(浏览器与本地进程通信)
    Chrome插件:微信公众号自动登录(chrome.extension)
    Chrome插件:浏览器后台与页面间通信
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/LG3626.html
Copyright © 2011-2022 走看看