2017国家集训队作业[arc082d]Sandglass
题意:
有一个沙漏,初始时(A)瓶在上方,两个瓶子的最大容量都为(X)克,沙子流动的速度为(1g)每单位时间。给出(K)个时间点(r_1sim r_K)表示在这几个时间点,漏斗会上下翻转,无视翻转时间。给出(Q)个询问,每个询问两个数(t_i,a_i),表示若初始时(A)瓶有(a_i)克沙子,询问第(t_i)单位时间时,(A)瓶中会有多少克沙子?((X,t_i,r_ileq 10^9,a_ileq X,Kleq 10^5),输入保证序列({r_i})和({t_i})为升序)
题解:
最后半小时想出来,真的惨。
观察发现(A)瓶中的沙子函数图像由若干个斜率为(1,-1,0)的一次函数组成。若存在某一时刻,该函数的斜率为(0)(即碰到上下边界),那么在此以后,函数的图像与截距((a_i))无关。
初始截距((a_i))的取值会使该函数出现三种情况:
(1.)只会碰到上边界
(2.)只会碰到下边界
(3.)上下边界都不会碰到
还存在一种情况:无论(a_i)的值是什么,该函数总会碰到上或下边界。
分别维护(a)的区间即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,l,r) for(int i=l;i<=r;i++)
#define of(i,l,r) for(int i=l;i>=r;i--)
#define fe(i,u) for(int i=head[u];i;i=e[i].next)
using namespace std;
typedef long long ll;
inline int rd()
{
static int x,f;
x=0,f=1;
char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
const int N=100010;
int X,K,Q,T[N],ty;
struct cha{
int a1,x1,a2,x2,x;
bool flag;
inline int get(int a)
{
if(flag)return x;
if(a<=a1)return x1;
if(a>=a2)return x2;
return a-a1+x1;
}
}c[N];
inline void gao(int i,int tim)
{
if(c[i].flag){
c[i].x+=ty*tim;
c[i].x=max(0,c[i].x);
c[i].x=min(c[i].x,X);
return;
}
if(ty==-1){
if(c[i].x2<=tim){
c[i].flag=1;c[i].x=0;
return;
}
if(tim>=c[i].x1){c[i].a1+=tim-c[i].x1;c[i].x1=0;c[i].x2-=tim;}
else{c[i].x1-=tim;c[i].x2-=tim;}
}
else{
if(c[i].x1+tim>=X){
c[i].flag=1;c[i].x=X;
return;
}
if(c[i].x2+tim>=X){c[i].a2-=c[i].x2+tim-X;c[i].x2=X;c[i].x1+=tim;}
else{c[i].x1+=tim;c[i].x2+=tim;}
}
}
int main()
{
freopen("inc.txt","r",stdin);
X=rd();K=rd();
c[0].a1=c[0].x1=0;c[0].a2=c[0].x2=X,c[0].x=0;
c[0].flag=0;ty=-1;
fo(i,1,K)T[i]=rd(),c[i]=c[i-1],gao(i,T[i]-T[i-1]),ty=-ty;
int j=0;
Q=rd();ty=-1;
fo(i,1,Q){
int t=rd(),a=rd();
while((j<K&&T[j+1]<=t))j++,ty=-ty;
int ans=c[j].get(a);
ans+=ty*(t-T[j]);
ans=min(ans,X);
ans=max(ans,0);
printf("%d
",ans);
}
return 0;
}
UPD:2018/8/19 补上了代码