1. 程式人生 > 其它 >「CEOI2010」 MP3Player 題解

「CEOI2010」 MP3Player 題解

「CEOI2010」 MP3Player

題意

\(~~~~\) 給出 \(n\) 次對音量的操作,每次操作為使音量 \(+1\)\(-1\) ,同時若音量操作後超出 \([0,V_{\max}]\) 或該操作與上一個操作的時間間隔 \(>t\) 則該操作無效(不論上一個操作是否有效)。已知若干次操作後的最終音量,求 \(t\) 可能的最大值以及該最大值下可能的最大的初始音量。

\(~~~~\) \(1\leq n\leq 2\times 10^5,2\leq 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^2\log 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(n\log 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\n",P[i].Time-1,Calc())&0;
	}
	return 0;
}