The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online J - Press the Button(思惟)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4056ios

題意

有一個按鈕、一個燈、一個計時器和一個計數器,每按一次按鈕,計時器被置爲v+0.5,若當前燈是滅的,按一次後變亮,若當前燈是亮的,按一次後計數器+1,若當前時間nt%a=0,則按b次按鈕,若nt%c=0,則按d次按鈕,問最後時間爲t時,計數器的值c++

分析

經過分析能夠發現,每lcm(a,c)個時間構成一個循環,因此只須要求出每一個lcm(a,c)的貢獻,做爲一段,再求出共有多少段便可,最後餘下部分不足一段再單獨處理,須要注意的是幾個邊界狀況,t=0時燈是滅的,因此第一段必定是以燈熄滅狀態爲起始條件,但後面的段可能以燈亮着狀態爲起始條件,這樣總體就會差1。
再分析會發現,對於每一段,咱們根本不用求出具體貢獻,只要先假設燈一直是亮的,求出所有貢獻,再求出燈熄滅了多少次就行,每熄滅一次counter數就要-1
能夠獲得這樣的公式:(t/a)*b+(t/c)*d+b+d-1 ,即表示從0~t時刻,假設燈一直是亮的,按鈕被按下的次數,由於實際t=0時燈是滅的,就至關於少按了一次按鈕
而後減去每一段燈熄滅的次數,再處理下結尾部分便可spa

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
vector<long long>Q;
long long a,b,c,d,v,t;
long long pt,cntt;
LL gcd(LL a,LL b){ //最大公約數if(b==0)  return a;
    else return gcd(b,a%b);
}

LL lcm(LL a,LL b){  //最小公倍數return a/gcd(a,b)*b;    //防止溢出
}
void getblock(){  //獲得每一段的貢獻,即一段中燈熄滅次數
    pt=lcm(a,c);
    cntt=0;
    Q.clear();
    for(LL i=0;i<=pt;i+=a) Q.push_back(i);
    for(LL i=0;i<=pt;i+=c) Q.push_back(i);
    sort(Q.begin(),Q.end());
    Q.erase(unique(Q.begin(),Q.end()),Q.end()); //去重

    for(int i=1;i<Q.size();i++){
        if(Q[i]-Q[i-1]>v)  //先後差大於v,即表示燈熄滅
            cntt++;
    }
}
int main(){
    std::ios::sync_with_stdio(false);
    int cnt;
    cin>>cnt;
    while(cnt--){
        cin>>a>>b>>c>>d>>v>>t;
        getblock();
        long long ans=(t/a)*b+(t/c)*d+b+d-1;    //從0~t按下的次數
        long long blk=t/pt;    //共有blk段
        ans=ans- blk*cntt;      //減去每段中燈熄滅的次數
        long long last=t%pt;        //最後不足一段時的邊界
        for(LL i=1;Q[i]<=last;i++)      //單獨處理最後不足一段部分
            if(Q[i]-Q[i-1]>v) ans--;
        cout<<ans<<endl;
    }
    return 0;
}
相關文章
相關標籤/搜索