三場組隊賽總結

省賽前最後的三場組隊賽了 形勢不妙 感覺一些題是應該要出的:

第一場的H題 uvalive-6852 

https://vjudge.net/contest/223856#problem/H

題意:給n個數從1到n 給一個區間a b 表示數字前a個能覆蓋 後b個不能 循環下去 問最後被覆蓋了(0,1,2,3)次的數字有多少個 

n到1e5 m到1e6 當時a和b都是小於等於16的

這道題的關鍵在於a和b都是小於等於16的 就是說總數不超128 但是常數128還是太大了 所以還要縮減常數 方法就是用一個map【b】【a】 其中map【b】【0-a】中的值都爲1 這樣你每次查詢時直接某個數模b就知道他是否位於哪個有值的區間了


第二場的F題 CodeChef - AMBOXES

題意 給一個大等級盒子 裏面很多個小等級盒子 等級爲0的盒子裏面纔有糖果 問要取出一定的糖果需要打開糖果多少次

  • 1 ≤ n,m ≤ 300000
  • 1 ≤ ai ≤ 109
  • 1 ≤ Xi ≤ 1012

這是數據範圍

感覺這道題和上面那題很像 關鍵點都在於要發現一個地方:數據並沒有並沒有由n推演的那麼大 他實際只有xi小於等於1e12 打比賽的時候我們想到非常暴力的就是從樹的底部遞推 每次用ceil函數疊加父節點個數 然而發現那樣如果直接嵌套循環的話複雜度是O(n*m) 實際上對於每次詢問我們不需要遍歷n次 只需要不斷的對當前的節點數相除 當他爲1時直接把前綴的長度和加上即可 因爲每次我的父節點至少衍生出兩個子節點 所以複雜度不會超過O(m*log(n)) 就是隻要在原來的超時代碼做小小的優化就行了

貼下代碼:

#include <set>
#include <map>
#include <list>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <bitset>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define ll long long
const int maxn=300000+500;

int tot;
int n,m;
ll a[maxn],len[maxn],inp[maxn];
ll sum[maxn];

inline void solve()
{
    ll x,ans=0;
    scanf("%lld",&x);
    for(int i=tot;i>=1;i--)
    {
        ans+=len[i]*x;
        x=x/a[i]+(x%a[i]==0?0ll:1ll);
        if(x==1ll)
        {
            ans+=sum[i-1];
            break;
        }
    }
    printf("%lld\n",ans);
}

int main()
{
    cin>>n>>m;
    tot=1;len[tot]=1;a[tot]=1;
    for(int i=1;i<=n;i++) scanf("%lld",&inp[i]);
    for(int i=n;i>=1;i--)
    {
        ll x=inp[i];
        if(x==1ll) len[tot]++;
        else
        {
            ++tot;
            a[tot]=x;
            len[tot]=1;
        }
    }
    len[tot]--;
    sum[0]=0;
    for(int i=1;i<=tot;i++) sum[i]=sum[i-1]+len[i];

    for(int i=1;i<=m;i++) solve();
    return 0;
}
未完。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章