zoukankan      html  css  js  c++  java
  • 【洛谷P1712】【LOJ2086】区间【线段树】【双指针】

    题目大意:

    题目链接:
    洛谷:https://www.luogu.org/problemnew/show/P1712
    LOJ:https://loj.ac/problem/2086

    nn个给定区间中选择mm个区间,要求这mm个区间共同包含至少一个位置,求所有方案中所选区间长度最大和最小之差尽量小的方案。


    思路:

    转换一下题意:如果能选出mm个区间使得一个位置选择mm次,那么这个就是一个合法的方案。
    为了方便求最终的答案,我们先把区间按长度来排序,这样若找到一个合法答案就只要用后者长度减去前者长度即可。
    这样我们就可以用双指针来模拟这道题了。不断右移指针rr,然后用cnt[i]cnt[i]表示位置ii被选则的次数,如果cnt[i]cnt[i]中有一位大于等于mm,那么就不断右移指针ll,直到cnt[i]cnt[i]中没有任何一位大于等于mm
    这样的时间复杂度是O(n2)O(n^2)的,不够优秀。
    我们发现,要快速查询某个数被选择了多少次其实是可以用线段树做的。维护线段树中的出现次数最多的数字以及lazylazy标记,每次右移指针时只要简单维护一下maxnmaxnlazylazy就行了。
    由于指针的均摊复杂度是O(1)O(1),所以总时间复杂度是O(nlogn)O(nlog n)的,足以过掉本题。


    代码:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N=500010,Inf=2e9;
    int n,m,maxn,ans=Inf,b[N*2];
    
    struct node
    {
    	int l,r,len;
    }a[N];
    
    struct Tree
    {
    	int l,r,maxn,lazy;
    }tree[N*8];
    
    bool cmp(node x,node y)
    {
    	return x.len<y.len;
    }
    
    void build(int x)
    {
    	if (tree[x].l==tree[x].r) return;
    	int mid=(tree[x].l+tree[x].r)/2;
    	tree[x*2].l=tree[x].l;
    	tree[x*2].r=mid;
    	tree[x*2+1].l=mid+1;
    	tree[x*2+1].r=tree[x].r;
    	build(x*2); build(x*2+1);
    }
    
    void pushdown(int x)
    {
    	if (tree[x].lazy)
    	{
    		tree[x*2].lazy+=tree[x].lazy;
    		tree[x*2+1].lazy+=tree[x].lazy;
    		tree[x*2].maxn+=tree[x].lazy;
    		tree[x*2+1].maxn+=tree[x].lazy;
    		tree[x].lazy=0;
    	}
    }
    
    void add(int x,int l,int r)
    {
    	if (tree[x].l==l && tree[x].r==r)
    	{
    		tree[x].lazy++;
    		tree[x].maxn++;
    		return;
    	}
    	pushdown(x);
    	int mid=(tree[x].l+tree[x].r)/2;
    	if (r<=mid) add(x*2,l,r);
    	else if (l>mid) add(x*2+1,l,r);
    	else add(x*2,l,mid),add(x*2+1,mid+1,r);
    	tree[x].maxn=max(tree[x*2].maxn,tree[x*2+1].maxn);
    }
    
    void del(int x,int l,int r)
    {
    	if (tree[x].l==l && tree[x].r==r)
    	{
    		tree[x].lazy--;
    		tree[x].maxn--;
    		return;
    	}
    	pushdown(x);
    	int mid=(tree[x].l+tree[x].r)/2;
    	if (r<=mid) del(x*2,l,r);
    	else if (l>mid) del(x*2+1,l,r);
    	else del(x*2,l,mid),del(x*2+1,mid+1,r);
    	tree[x].maxn=max(tree[x*2].maxn,tree[x*2+1].maxn);
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&a[i].l,&a[i].r);
    		a[i].len=a[i].r-a[i].l;
    		b[2*i-1]=a[i].l; b[2*i]=a[i].r;
    	}
    	sort(b+1,b+1+n+n);
    	int tot=unique(b+1,b+1+n+n)-b-1;
    	for (int i=1;i<=n;i++)
    	{
    		a[i].l=lower_bound(b+1,b+1+tot,a[i].l)-b;
    		a[i].r=lower_bound(b+1,b+1+tot,a[i].r)-b;
    		if (a[i].r>maxn) maxn=a[i].r;
    	}
    	tree[1].l=1; tree[1].r=maxn;
    	build(1);
    	sort(a+1,a+1+n,cmp);
    	for (int r=1,l=0;r<=n;r++)
    	{
    		add(1,a[r].l,a[r].r);
    		if (tree[1].maxn>=m)
    		{
    			do
    			{
    				l++;
    				del(1,a[l].l,a[l].r);
    			}while (tree[1].maxn>=m);
    			if (a[r].len-a[l].len<ans) ans=a[r].len-a[l].len;
    		}
    	}
    	if (ans<Inf) printf("%d
    ",ans);
    		else printf("-1");
    	return 0;
    }
    
  • 相关阅读:
    20120110 自己写的基于jquery的翻页效果
    搜来的 可爱的if ie
    哎~~~纠结死了的终于解决的i6的fixed属性
    2011815发现可好的js繁简转换代码 写这些的人。。好厉害呀 收藏了~~~
    网站制作CSS图片转换滤镜代码(貌似只对ie有用。。。)
    2011811 右下角弹出渐隐的广告代码 可以关闭 可以缩小 还各种兼容(ff opera ie6、7、8都试过了)值得保留哎~~~
    20120604 自己写的基于jquery的类似百度贴吧头像提示效果
    UFT textUtil object 解决奇怪问题
    UFT send request & get response
    UFT connect sql (1)
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998107.html
Copyright © 2011-2022 走看看