• AtCoder Beginner Contest 196 E


    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
    ",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
    ",now+tmp);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    查询Python支持的.whl格式
    内置模块之os
    内置标准库之time
    迭代器(iterator)
    STM32F103和SIM800L打造低成本短信转发系统(五):控制程序
    STM32F103和SIM800L打造低成本短信转发系统(四):MDK-ARM
    STM32F103和SIM800L打造低成本短信转发系统(三):STM32CubeMX
    STM32F103和SIM800L打造低成本短信转发系统(二):软件安装篇
    STM32F103和SIM800L打造低成本短信转发系统(一):硬件篇
    处理SIM800L模块的时间字符串之URL编码(百分号编码)
  • 原文地址:https://www.cnblogs.com/Ritalc/p/14574950.html
Copyright © 2020-2023  润新知