【AtCoder Regular Contest 082 F】Sandglass

【链接】点击打开链接


【题意】


你有一个沙漏。
沙漏里面总共有X单位的沙子.
沙漏分A,B上下两个部分。
沙漏从上半部分漏沙子到下半部分。
每个时间单位漏1单位的沙子。
一开始A部分在上面。然后在r1,r2,....rk时刻,会把沙漏翻转一下.
给你Q个询问,每个询问两个数字ti,ai;
表示一开始A部分有ai个单位的沙子,问你ti时刻,A部分有多少沙子。

【题解】


我们设f[i]表示初始A部分有i个单位的沙子,t时刻A中剩余的沙子数目,并假设X=7;
我们先来模拟一下这个过程,右边的加号和减号表示沙子增加或减少(对应A在漏或在接沙子)
t=0 f[] = {0,1,2,3,4,5,6,7}
t=1 f[] = {1,2,3,4,5,6,7,7} +
t=2 f[] = {2,3,4,5,6,7,7,7} +
t=3 f[] = {3,4,5,6,7,7,7,7} +
t=4 f[] = {2,3,4,5,6,6,6,6} -
t=5 f[] = {1,2,3,4,5,5,5,5} -
t=6 f[] = {0,1,2,3,4,4,4,4} -
t=7 f[] = {0,0,1,2,3,3,3,3} -
t=8 f[] = {1,1,2,3,4,4,4,4} +
可以看到,f数组总是类似{a,a,a+1,a+2,a+3...b-2,b-1,b,b}这样的形式;
a和b会随着沙子减少或增加的时候发生改变.
而且变化都是有规律的,因为每个数字都递增1或递减1.
那么,如果我们能维护a和b的位置l和r的话,就能很容易得到答案了.
如果初始沙子ai<=l,则答案为a,如果ai >= r则答案为b,否则为a + ai - l;
我们可以按照ri的时间,一块一块地获取到ri时刻f数组的四个参数a,b,l,r;
(因为r[i-1]..r[i]这一段肯定都是减,或都是加,那么想要维护a,b,l,r就不会难了)
(只有在a减去时间差小于0或是b加上时间差大于X的时候,l和r才会变)
然后对于询问ti,ai,我们只要找到最大的j,使得rj <= ti;
这样,我们先通过rj时刻f数组的参数a,b,l,r来获取初始沙子量为ai,rj时刻A的沙子量temp.
然后rj..ti这一段则全是减或全是加,则temp直接加上或减去ti-rj就可以了,大于X则变成X,小于0则变成0.
做的时候,可以把询问和翻转操作的时间放在一起,然后排个序.
翻转操作,就维护a,b,l,r;如果是询问操作,就通过a,b,l,r求出初始沙子量为ai,时刻前一个翻转点A的沙子量temp.
再加上ti-前一个反转点的时间就是所需的答案了。


【错的次数】


1

【反思】


初始沙子量为0..X;
然后时间为t时,对应沙子量随着t的递增,是有规律的.
因为每种初始沙子量,它随着时间,在某个区间内都是递增1,或都是递减1.
这样,在整块都是递减,或整块都是递增的时间里,我们能快速维护,初始沙子量为0..X,对应时间的沙子量.
(不用求出来,只要通过维护4个变量就能O(1)处理询问);
虽然询问是单点的.
但只要能快速求出它前一个翻转的时间点,剩下的也能O(1)处理.
翻转的时间点之间就是整块的加或整块减的时间。
如果只有一个初始的沙子量,那么这题就是一道水题了。
但是因为多个沙子量,给实现造成了很大的困难.
因此通过模拟,发现初始沙子量不同的话,到某个时刻的沙子量是有规律的。
从而解决了有多个初始沙子量的问题。

【代码】

/*

*/
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
#include <iomanip>
#include <set>
#include <cstdlib>
#include <cmath>
#include <bitset>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb emplace_back
#define fi first
#define se second
#define ld long double
#define ms(x,y) memset(x,y,sizeof x)
#define ri(x) scanf("%d",&x)
#define rl(x) scanf("%lld",&x)
#define rs(x) scanf("%s",x)
#define rf(x) scnaf("%lf",&x)
#define oi(x) printf("%d",x)
#define ol(x) printf("%lld",x)
#define oc putchar(' ')
#define os(x) printf(x)
#define all(x) x.begin(),x.end()
#define Open() freopen("F:\rush.txt","r",stdin)
#define Close() ios::sync_with_stdio(0)
#define sz(x) ((int) x.size())
#define ld long double

typedef pair<int,int> pii;
typedef pair<LL,LL> pll;

//mt19937 myrand(time(0));
//int get_rand(int n){return myrand()%n + 1;}
const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
const double pi = acos(-1.0);
const int N = 2e5;

struct abc{
    int t,a,id;
    bool operator < (const abc & tmp) const{
        return t < tmp.t;
    }
};

int X,K,n,ans[N+10];
abc q[N+10];

int main(){
    //Open();
    //Close();
    ri(X),ri(K);
    rep1(i,1,K) {
        int x;
        ri(x);
        q[++n].t = x;
    }
    ri(K);
    rep1(i,1,K){
        int t,a;
        ri(t),ri(a);
        q[++n].t = t,q[n].a = a,q[n].id = i;
    }
    sort(q+1,q+1+n);
    int a = 0,b = X,l = 0,r = X,pre = 0,sign = -1;
    rep1(i,1,n)
        if (q[i].id){
            //是一个询问
            int rest = q[i].t - pre,temp;
            if (q[i].a <= l){
                temp = a;
            }else if (q[i].a >= r){
                        temp = b;
                  }else
                        temp = a + q[i].a - l;
            temp += sign*rest;
            if (temp > X) temp = X;
            if (temp < 0) temp = 0;
            ans[q[i].id] = temp;
        }else{
            //是一个翻转操作
            int v = q[i].t - pre;
            if (sign==-1){//如果要做减法
                if (a<v){//如果最小的那个不够减了,则肯定
                    b = max(b-v,0);
                    l = min(l + (v-a),X);
                    r = max(l,r);
                    a = 0;
                }else{
                    a-=v,b-=v;
                }
            }else{
                if (b+v<=X){//最大的加上v之后都小于等于X则可行,a+v<=x,a+1+v<=x...a+b-a+v<=x
                    a+=v,b+=v;
                }else{
                    //b+v>X
                    a = min(a+v,X);//a加上之后可能大于X了.最大让他为X
                    r = max(r - (b+v-X),0);//倒数第1+(b+v-X)个数到最后一个数都变成X了
                    l = min(l,r);//r要是小于l了,就把l变成r就好.
                    b = X;//b是肯定变成x了
                }
            }
            pre = q[i].t;
            sign = sign*(-1);
        }
    rep1(i,1,K)
        oi(ans[i]),puts("");
    return 0;
}



原文地址:https://www.cnblogs.com/AWCXV/p/7626052.html