三场组队赛总结

省赛前最后的三场组队赛了 形势不妙 感觉一些题是应该要出的:

第一场的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;
}
未完。。。

猜你喜欢

转载自blog.csdn.net/weixin_39302444/article/details/80072760