AtCoder Beginner Contest 196 E - Filters 题解

E - Filters 题解

题面

官方题解

题目大意

给n个a[i],t[i],给q个x[i]

fi(x)=x+ai(ti=1)

fi(x)=max(x,ai)(ti=2)

fi(x)=min(x,ai)(ti=3)

对于每个i(1<=i<=q), 求出 fN(…f2(f1(xi))…)

1<=n,q<=200,000

做法

以下两个做法题解中都给出了,但是做法二题解中没有给出推导过程和代码,所以本文着重对 做法二 做出 推导和说明

做法一

这个做法比较容易想出,是根据函数的图像得出的性质

  1. (t[i]==1)

    f[i]=x+a[i]

    图象是斜率等于一的一条直线

  2. (t[i]==2)

    f[i]=x,(x>=a[i])

    f[i]=a[i],(x<a[i])

    图象是分段函数,自变量从负无穷到x==a[i]时函数值是a[i],其余部分是一三象限角平分线的一部分

  3. (t[i]==3)

    f[i]=x,(x<=a[i])

    f[i]=a[i],(x>a[i])

    图象是分段函数,自变量从负无穷到x==a[i]时图象是一三象限角平分线的一部分,其余部分函数值等于a[i]

然后我们发现经过这些函数的加工后它有最小值(下界)和最大值(上界),遇到 t[i] == 1 时,下界和上界都加上a[i],并记录tmp t[i] == 1 时 a[i] 的和,最后把每输入的x加上tmp再处理

遇到 t[i] == 2 或 t[i] == 3 时 ,下界和上界都分别min/max上这个值(里层min,max函数的值域作外层函数的定义域,这个可以自己画图模拟一下过程)

给出代码

#include<bits/stdc++.h>
#define re register 
#define N 200010
#define int long long
#define inf 2e18
using namespace std;
int n,tmp,l,h,q;
int a[N],t[N];
template <class T> inline void read(T &x)
{
	x=0;int g=1;char ss=getchar();
	for (;ss>'9'||ss<'0';ss=getchar()) if (ss=='-') g=-1;
	for (;ss<='9'&&ss>='0';ss=getchar()) x=(x<<1)+(x<<3)+(ss^48);	
	x*=g;
}
inline int doit(int x)
{
	if (x<l) return l;if (x>h) return h;return x;
}
signed main()
{
	re int i,j,op,x;
	read(n);
	for (i=1;i<=n;i++)
		read(a[i]),read(t[i]);
	l=-inf,h=inf;
	for (i=1;i<=n;i++)
	{
		if (t[i]==1)	tmp+=a[i],l+=a[i],h+=a[i];
		else if (t[i]==2) l=max(l,a[i]),h=max(h,a[i]);
		else l=min(l,a[i]),h=min(h,a[i]);
	}
	read(q);
	while(q--)
	{
		read(x);
		printf("%lld\n",doit(x+tmp));
	}
	return 0;
}

做法二

看到题解给出的式子是不是很蒙

嗯我也是

实际上题解省略了很多步推导过程

下面我一一给出并证明解释(做法与题解稍有不同因为先预处理了 t[i]==1 的操作)

预处理

遇到 t[i] == 1 时,我们会想到把它后面的取 max/min 操作的值减去当前的 a[i] 其实和 x+a[i] 是等效的

再记录tmp t[i] == 1 时 a[i] 的和,在最终的答案加上tmp即可

如果遇到连续的取 max/min 可以把它们合并成一个,于是就成了max,min交替出现

引理

  1. min(min(x,y),z) <==> min(x,min(y,z))

  2. max(max(x,y),z) <==> max(x,max(y,z))

前两个引理比较显然,就是三个数取最大(小)值,先取出任意两个取大(小),再与剩下的一个数取大(小)

  1. min(x,max(y,z)) <==> max(min(x,y),min(x,z))

  2. max(x,min(y,z)) <==> min(max(x,y),max(x,z))

这两条引理同理,所以文中只说明引理 3

我们用分类讨论来说明它

其中y,z的大小关系并不需要讨论(它们可以互换)

假设 y <= z, 于是 min(x,max(y,z)) ==> min(x,z)

若 x <= y <= z ,两边的值都是 x

若 y <= z <= x ,两边的值都是 z

若 y <= x <= z ,两边的值都是 x

  1. min(x,y) <= max(x,z)

    max(min(x,y),max(x,z)) <==> max(x,z)

证明: min(x,y) <= x <= max(x,z)

推导过程

已知里层函数 f(x)=min(b1,max(a1,x)),外层函数 g(x)=min(b2,max(a2,x))

所以

g(f(x))

= min(b2,max(a2,min(b1,max(a1,x))))

由引理 3 有

= min(b2,max(a2,max(min(b1,a1),min(b1,x))))

由引理 1 有

= min(b2,max(min(b1,x),max(a2,min(b1,a1))))

此时我们令`T=max(a2,min(b1,a1))**

min(b2,max(min(b1,x),T))

由引理 4 有

= min(b2,min(max(T,b1),max(T,x)))

由引理 2 有

= min(min(b2,max(T,b1)),max(T,x)))

此时我们把前面的T展开

min(min(b2,max(max(a2,min(b1,a1)),b1)),max(T,x)))

由引理 1 有

= min(min(b2,max(min(b1,a1),max(a2,b1))),max(T,x)))

由引理 5 有

= min(min(b2,max(a2,b1)),max(T,x))

此时我们再把剩下的T展开

min(min(b2,max(a2,b1)),max(max(a2,min(b1,a1)),x))

看下这个最终的式子,是不是感觉眼熟

它的结构和f(x),g(x) 是相同的!!!

如果F(x)= min(min(b2,max(a2,b1)),max(max(a2,min(b1,a1)),x))

那么

b3= min(b2,max(a2,b1))

a3= max(a2,min(b1,a1))

于是两个嵌套的函数就可以变成一个啦

再然后多个嵌套的函数就可以变成一个啦

上代码

#include<bits/stdc++.h>
#define re register 
#define N 200010
#define int long long
using namespace std;
int n,q,cnt,e,h,tot;
int a[N],t[N],b[N],s[N],c[N],w[N];
template <class T> inline void read(T &x)
{
	x=0;int g=1;char ss=getchar();
	for (;ss>'9'||ss<'0';ss=getchar()) if (ss=='-') g=-1;
	for (;ss<='9'&&ss>='0';ss=getchar()) x=(x<<1)+(x<<3)+(ss^48);	
	x*=g;
}
signed main()
{
	re int i,j,now;
	read(n);
	for (i=1;i<=n;i++)
		read(c[i]),read(w[i]);
	int tmp=0; 
	for (i=1;i<=n;i++)
	{
		if (w[i]==1) 	tmp+=c[i];
		else c[i]-=tmp;
	}
	for (i=1;i<=n;i++)
	if (w[i]>1) 
	a[++tot]=c[i],t[tot]=w[i];
	for (i=2;i<=tot;i++)
	if (t[i]!=1&&t[i]==t[i-1])
	{
		if (t[i]==2)	a[i]=max(a[i],a[i-1]);
		else	a[i]=min(a[i],a[i-1]);
		t[i-1]=0;
	}
	for (i=1;i<=tot;i++) if (t[i]>1) b[++cnt]=a[i],s[cnt]=t[i];
	if (s[1]==3) h=2;
	else h=1;
	int x=b[h],y=b[h+1];
	for (i=h+2;i<cnt;i+=2)
	if (i<cnt)
	{
		int ty=y,tx=x;
		x=max(b[i],min(ty,tx));
		y=min(b[i+1],max(b[i],ty));
	}
	if (s[cnt]==2) e=cnt-1;
	else e=cnt;
	read(q);
	while(q--)
	{
		read(now);
		if (h!=1) now=min(now,b[1]);
		if (h<e) now=min(y,max(x,now));
		if (e!=cnt) now=max(now,b[cnt]);
		printf("%lld\n",now+tmp);
	}
	return 0;
}

posted @ 2021-03-25 12:37  Ritalc  阅读(182)  评论(0编辑  收藏  举报