zoukankan      html  css  js  c++  java
  • 「CEOI2010」 MP3Player 题解

    「CEOI2010」 MP3Player

    本文版权归Azazel与博客园共有,欢迎转载,但需保留此声明,并给出原文地址,谢谢合作。

    原文地址:https://www.cnblogs.com/Azazel/p/14999820.html

    题意

    (~~~~) 给出 (n) 次对音量的操作,每次操作为使音量 (+1)(-1) ,同时若音量操作后超出 ([0,V_{max}]) 或该操作与上一个操作的时间间隔 (>t) 则该操作无效(不论上一个操作是否有效)。已知若干次操作后的最终音量,求 (t) 可能的最大值以及该最大值下可能的最大的初始音量。

    (~~~~) (1leq nleq 2 imes 10^5,2leq V_{max}leq 5000)

    题解

    (~~~~) 线段树可以维护的东西增加了,线段树yyds

    (~~~~) 先给出一个暴力通用思路:枚举所有在操作中出现的 (t) ,则对于任意一个初始音量 (V_1),我们都可以 (mathcal{O(n)})求出其对应的结束音量 (V_2) 。并且输出出来我们可以发现若将 (V_2) 视作 (V_1) 的函数,则该函数不降,故可以二分求出是否存在 (V_1) 在该 (t) 下使得结束音量为 (V_2) 。时间复杂度:(mathcal{O(n^2log n)}) 似乎一个subtask都过不了(

    (~~~~) 考虑继续发掘一下该函数的性质,可以发现该函数存在 (A,B) ,使得 ([0,A)) 内所有 (V_1)(V_2) 相同,([A,B)) 内所有 (V_1)(V_2) 每个相对前一个多 (1)([B,V_{max}]) 内所有 (V_1)(V_2) 也相同。简单来说该函数依次是:常函数、一次函数、常函数。理性理解一下, (V_1) 在从 (0) 开始增长时中途一定会有一段时间卡在下界、增长到 (V_{max}) 前有一段时间卡在上界,当然这段时间可以为空。那么显然,该函数的值域也一定是连续的整数区间。

    (~~~~) 由此,我们可以考虑维护两个常函数的值 (a,b) 和一次函数的 (y=x+c)(c) 。则当我们知道两段相连区间的 (a,b,c) 时,我们就可以考虑复合两个函数得到这个大区间的 (a,b,c) 。稍微手玩一下就可以推出 (mathcal{O(1)}) 转移,这点可以见代码。自然而然地,我们想到用线段树维护区间的函数值。

    (~~~~) 因此,我们仍然保留枚举所有可能的 (t) ,然后每次在线段树上删除某个点对应的贡献,并检查该函数值域中是否有 (V_2) 。该过程中每个点只会被删去一次,所以时间复杂度 (mathcal{O(nlog n)})

    代码

    查看代码
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int n,Vmax,V2;
    int val[200005],Time[200005];
    struct node{
    	int val,Time,id;
    	node(){}
    	node(int V,int T,int I){val=V,Time=T,id=I;}
    }P[200005];
    struct Seg{
    	#define ls p<<1
    	#define rs p<<1|1
    	#define lson p<<1,l,mid
    	#define rson p<<1|1,mid+1,r
    	struct Func{
    		int a,b,c;
    		Func(){}
    		Func(int A,int B,int C){a=A,b=B,c=C;}
    		int F(int x){return min(b,max(a,x+c));}
    	}tr[800005];
    	//a一平的值 b二平的值 c一次函数 y=x+c 
    	void pushUp(int p)
    	{
    		tr[p].a=min(tr[rs].b,max(tr[rs].a,tr[ls].a+tr[rs].c));
    		tr[p].b=max(tr[rs].a,min(tr[rs].b,tr[ls].b+tr[rs].c));
    		tr[p].c=tr[ls].c+tr[rs].c;
    	}
    	void Build(int p,int l,int r)
    	{
    		if(l==r)
    		{
    			tr[p]=Func(0,Vmax,val[l]);
    			return;
    		}
    		int mid=(l+r)>>1;
    		Build(lson);Build(rson);
    		pushUp(p);
    	}
    	void Modify(int p,int l,int r,int aim)
    	{
    		if(l==r)
    		{
    			tr[p]=Func(0,Vmax,0);
    			return;
    		} 
    		int mid=(l+r)>>1;
    		if(aim<=mid) Modify(lson,aim);
    		if(mid<aim)  Modify(rson,aim);
    		pushUp(p);
    	} 
    }Seg;
    int Calc()
    {
    	int l=0,r=Vmax,mid,Ans=0;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(Seg.tr[1].F(mid)<=V2) l=mid+1,Ans=mid;
    		else r=mid-1;
    	}
    	return Ans;
    }
    bool cmp(node x,node y){return x.Time>y.Time;}
    int main() {
    	scanf("%d %d %d",&n,&Vmax,&V2);
    	char S[10];
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%s %d",S+1,&Time[i]);
    		val[i]=(S[1]=='+')?1:-1;
    	}n--;
    	for(int i=1;i<=n;i++)
    	{
    		val[i]=val[i+1];
    		Time[i]=Time[i+1]-Time[i];
    		P[i]=node(val[i],Time[i],i);
    	}
    	Seg.Build(1,1,n);
    	sort(P+1,P+1+n,cmp);
    	if(Seg.tr[1].F(0)<=V2&&V2<=Seg.tr[1].F(Vmax)) return puts("infinity")&0;
    	int j;
    	for(int i=1;i<=n;i=j)
    	{
    		int Tmp=P[i].Time;
    		for(j=i;P[j].Time==P[i].Time;j++) Seg.Modify(1,1,n,P[j].id);
    		if(Seg.tr[1].F(0)<=V2&&V2<=Seg.tr[1].F(Vmax)) return printf("%d %d
    ",P[i].Time-1,Calc())&0;
    	}
    	return 0;
    }
    
  • 相关阅读:
    thinkphp3.2 无法加载模块
    php 使用 wangeditor3 图片上传
    nginx 配置 server
    oracle练手(一)
    Oracle练习(一)
    java运算符优先级
    数据库(mysql和oracle)
    java实现4种内部排序
    mysql-----分库分表
    NIO总结-----Buffer
  • 原文地址:https://www.cnblogs.com/Azazel/p/14999820.html
Copyright © 2011-2022 走看看