省賽前最後的三場組隊賽了 形勢不妙 感覺一些題是應該要出的:
第一場的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;
}
未完。。。