1 条题解
-
0
「CEOI2010」 MP3Player
题意
给出 次对音量的操作,每次操作为使音量 或 ,同时若音量操作后超出 或该操作与上一个操作的时间间隔 则该操作无效(不论上一个操作是否有效)。已知若干次操作后的最终音量,求 可能的最大值以及该最大值下可能的最大的初始音量。
题解
线段树可以维护的东西增加了,线段树yyds先给出一个暴力通用思路:枚举所有在操作中出现的 ,则对于任意一个初始音量 ,我们都可以 求出其对应的结束音量 。并且输出出来我们可以发现若将 视作 的函数,则该函数不降,故可以二分求出是否存在 在该 下使得结束音量为 。时间复杂度:
似乎一个subtask都过不了(考虑继续发掘一下该函数的性质,可以发现该函数存在 ,使得 内所有 的 相同, 内所有 的 每个相对前一个多 , 内所有 的 也相同。简单来说该函数依次是:常函数、一次函数、常函数。理性理解一下, 在从 开始增长时中途一定会有一段时间卡在下界、增长到 前有一段时间卡在上界,当然这段时间可以为空。那么显然,该函数的值域也一定是连续的整数区间。
由此,我们可以考虑维护两个常函数的值 和一次函数的 的 。则当我们知道两段相连区间的 时,我们就可以考虑复合两个函数得到这个大区间的 。稍微手玩一下就可以推出 转移,这点可以见代码。自然而然地,我们想到用线段树维护区间的函数值。
因此,我们仍然保留枚举所有可能的 ,然后每次在线段树上删除某个点对应的贡献,并检查该函数值域中是否有 。该过程中每个点只会被删去一次,所以时间复杂度 。
代码
#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\n",P[i].Time-1,Calc())&0; } return 0; }
- 1
信息
- ID
- 3676
- 时间
- 500ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者