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
做法
以下两个做法题解中都给出了,但是做法二题解中没有给出推导过程和代码,所以本文着重对 做法二 做出 推导和说明
做法一
这个做法比较容易想出,是根据函数的图像得出的性质
-
(t[i]==1)
f[i]=x+a[i]
图象是斜率等于一的一条直线
-
(t[i]==2)
f[i]=x,(x>=a[i])
f[i]=a[i],(x<a[i])
图象是分段函数,自变量从负无穷到x==a[i]时函数值是a[i],其余部分是一三象限角平分线的一部分
-
(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
",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交替出现
引理
-
min(min(x,y),z) <==> min(x,min(y,z))
-
max(max(x,y),z) <==> max(x,max(y,z))
前两个引理比较显然,就是三个数取最大(小)值,先取出任意两个取大(小),再与剩下的一个数取大(小)
-
min(x,max(y,z)) <==> max(min(x,y),min(x,z))
-
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
- 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
",now+tmp);
}
return 0;
}