看板娘加载较慢请耐心等待qwq~~~

2019.9.19模拟赛A组

HOJ1034遭遇(2019.9.19模拟赛A组T1)

404nofind

题意

lky要跳楼
你需要一开始选择一座楼,开始跳楼。在第\(i\)座楼准备跳楼需要\(c_i\)的花费。每次可以跳到任何一个还没有跳过的楼上去。但跳楼是有代价的,每次跳到另外一座楼的代价是两座楼高度的差的绝对值,最后一次从楼上跳到地面上不需要代价(只能跳到地上一次)。求在代价不超过\(T\)的情况下,最多跳几次楼。(一座楼只能跳一次,且每次跳楼都要计算准备的花费)

怎么做

贪心+DP

由题意可知,每次跳跃要消耗高度差的代价。这样如果反复横跳肯定是不优的,为了保证高度差的代价尽量小,我们应采用按照从高到低或从低到高的顺序跳楼。

f[i][j]跳了j栋到i的最小代价
DP阶段:跳到了i
状态:跳了j栋
转移方程:f[i][j] = min(f[i][j], f[k][j-1]);
复杂度\(O(n^3)\)
其实是可以优化成\(O(n^2)\)的,只是我不会。又要咕咕咕这个做法了

代码

#include <bits/stdc++.h>
namespace fdata
{
inline char nextchar()
{
    static const int BS = 1 << 21;
    static char buf[BS], *st, *ed;
    if (st == ed)
        ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename T>
inline T poread()
{
    T ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;

    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
    return ret;
}
template <typename Y>
inline void poread(Y &ret)
{
    ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;

    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
using namespace std;
#define min(_x, _y) (_x > _y ? _y : _x)
const int MAXN = 305;
int f[MAXN][MAXN];
struct node
{
    int h, c;
    bool operator<(const node &it) const
    {
        return h < it.h;
    }
} building[MAXN];
#define h(_o) building[_o].h
#define c(_o) building[_o].c
int n, t;
int main()
{
    poread<int>(n);
    for (register int i = 1; i <= n; ++i)
        poread<int>(building[i].c);
    for (register int i = 1; i <= n; ++i)
        poread<int>(building[i].h);
    poread<int>(t);
    sort(building + 1, building + 1 + n);
    memset(f, 0x3f, sizeof(f));
    for (register int i = 1; i <= n; ++i)
        f[i][0] = 0;
    for (register int i = 1; i <= n; ++i)
    {
        for (register int j = 1; j <= i + 1; ++j)
        {
            for (register int k = 1; k < i; ++k)
            {
                f[i][j] = min(f[i][j], f[k][j - 1] + h(i) - h(k) + c(k));
            }
        }
    }
    int ans = 0;
    for (register int i = n; ~i; --i)
    {
        for (register int j = 1; j <= n; ++j)
        {
            if (f[j][i] + c(j) <= t)
            {
                printf("%d\n", i + 1);
                return 0;
            }
        }
    }
}

HOJ1035都市(2019.9.19模拟赛A组T2)(Luogu3540SQU-Squarks)

\(\color{blue}{\text{404nofind}}\)

题意

有n个数,给出两两之间的和,求这n个数的所有可能值。

怎么做

这是智商题
考虑一个事情,如果给定三个数\(a_1, a_2, a_3\)分别相加的和,怎样求出这三个数呢?
显然,我们可以算出来这三个数的和,即\(\frac{(a_1 + a_2)+(a_1+a_3)+(a_2+a_3)}{2} = a_1+a_2+a_3\),并依次相减,即可得到\(a_1, a_2, a_3\)
那么对于\(n\)个数{\(a_1, a_2...a_n\)}, 若我们钦定数列是有序的,我们可以判断出\(a_1+a_2\)以及\(a_1+a_3\),因为这两个分别是最小的以及第二小的数。

证明:
假设\(a_1+a_2>a_x+a_y(x, y \neq 1, 2)\)
\(\because a_2<a_x\)
\(\therefore a_1+a_2>a_2+a_y\)
\(\qquad \quad a_1 > a_y\)
这与{\(a_1, a_2...a_n\)}有序不符,所以\(a_1+a_2\)是最小的,同理可证\(a_1+a_3\)第二小。

此时我们已经知道了\(a_1+a_2\)以及\(a_1+a_3\),在剩下的情况中枚举\(a_2+a_3\),就可以求出\(a_1, a_2, a_3\)这三个数。
在求得\(a_1\)后,我们便可枚举\(a_1+a_k\)是哪个数,并且判断该情况是否合法。以上两个枚举复杂度都是O(\(n\)).
关于如何判断是否合法,可以通过已知的数,进行求和,并判断这个情况下的和是否符合题目给出的值。这个复杂度是O(\(n\))的。
整体复杂度O(\(n^3\))。

放代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 305;
int n, m, cnt;
int a[45005];
bool use[45005];
int num[MAXN];
struct node
{
    int ans[MAXN];
    inline void print()
    {
        for (register int i = 1; i <= n; ++i)
            printf("%d ", ans[i]);
        putchar('\n');
    }
    void operator=(int *s)
    {
        memcpy(ans, s, sizeof(ans));
    }
} ans[MAXN];
inline int find(const int &x)
{
    int l = 1, r = m, mid, res;
    while (l <= r)
    {
        mid = (l + r) >> 1;
        if (a[mid] <= x)
        {
            res = mid, l = mid + 1;
        }
        else
        {
            r = mid - 1;
        }
    }
    return res;
}
inline void check(int x)
{
    memset(use, 0, sizeof(use));
    int sum = a[1] + a[2] + a[x];
    if (sum & 1)
        return;
    sum >>= 1;
    num[1] = sum - a[x];
    num[2] = sum - a[2];
    num[3] = sum - a[1];
    use[1] = use[2] = use[x] = true;
    for (register int i = 3, k = 4; k <= n; ++k) //枚举a1 + ak
    {
        while (i <= m && use[i]) //判重
            ++i;
        if (i > m)
            return;
        num[k] = a[i] - num[1];
        use[i] = true;
        for (register int j = 2; j < k; ++j) //枚举a[x] + a[k]
        {
            if (num[j] > num[k]) //保证顺序
                return;
            sum = num[j] + num[k];
            register int p = find(sum);
            if (a[p] != sum)
                return;
            register int px = p;
            while (px && a[px] == a[p]) //找到相同数值中最前面的一个
                --px;
            ++px;
            while (px <= m && use[px] && a[px] == a[p])
                ++px;
            if (a[px] != a[p] || use[px])
                return;
            p = px;
            use[p] = true;
        }
    }
    ++cnt;
    ans[cnt] = num;
}
int main()
{
    scanf("%d", &n);
    m = (n * (n - 1)) / 2;
    for (register int i = 1; i <= m; ++i)
    {
        scanf("%d", &a[i]);
    }
    sort(a + 1, a + m + 1);
    for (register int i = 3; i <= m;) //枚举a2 + a3;
    {
        check(i);
        register int ii = i;
        while (a[ii] == a[i]) //去重
            ++ii;
        i = ii;
    }
    printf("%d\n", cnt);
    for (register int i = 1; i <= cnt; ++i)
        ans[i].print();
}

小结

考试的时候这个题只想到了通过三个数的和求出这三个数,应该考虑将其扩展进行枚举。

HOJ1036街灯(2019.9.19模拟赛A组T3)

404nofound

题意

街上的街灯亮起,指引向着远方的路。每个街灯上都有一个数,每次询问,第\(l\)个街灯到第\(r\)个街灯上的数模\(p\)等于\(v\)的有几个。

咋做

\({n\sqrt{n}\log{n}}\) 在线做法

这种题考虑分成部分处理。对于\({q<100}\)的部分,它的模数不超过100,所以可以直接开一个桶\(v_1[i][j]\)记录\(i % p == j\) 的数的出现位置,询问时查询出这个桶中有几个位置在\(l,r\)之间即可。由于数据范围较大,这个桶开成vector,保证不炸空间。
对于剩下的部分,我们发现如果\({a \equiv v{\pmod p} }\), \(a = p * i + v\), 我们枚举\(i\),便可以找到所有符合询问要求的数。所以开一个桶\(v_2[x]\),放入所有\(x\)的出现位置,查询时枚举\(i\), 使 $ x = p*i+v \(, 找到在\)l,r\(区间内有几个\)x\(,并对所有x的答案求和即为所求。<br>复杂度分析:每个询问,由于枚举\)x\(是\)\sqrt{n}\(的,查询区间内的数二分左右端点是\)\log{n}\(的,总体复杂度\)O({n\sqrt{n}\log{n}})$。

\({n\sqrt{n}}\) 离线做法

和之前的做法差不多,只是要离线按照位置\(sort\)一下询问,处理\(1\) ~ \(l-1\)\(1\)~\(r\),答案为二者的差。由于不用二分,所以复杂度是\({O(n\sqrt{n})}\)

Code

这个是\(O({n\sqrt{n}\log{n}})\)在线的

#include <bits/stdc++.h>
namespace fdata
{
inline char nextchar()
{
    static const int BS = 1 << 21;
    static char buf[BS], *st, *ed;
    if (st == ed)
        ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename T>
inline T poread()
{
    T ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;
    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
    return ret;
}
template <typename Y>
inline void poread(Y &ret)
{
    ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;

    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
using namespace std;
const int MAXN = 105;
int n, m;
struct node
{
    vector<int> v;
    inline int find(const int &x, int &rec)
    {
        register int l = 0, r = v.size() - 1, mid;
        while (l <= r)
        {
            mid = (l + r) >> 1;
            if (v[mid] >= x)
            {
                rec = mid;
                r = mid - 1;
            }
            else
            {
                l = mid + 1;
            }
        }
        return rec;
    }
    inline int finds(const int &x, int &rec)
    {
        register int l = 0, r = v.size() - 1, mid;
        while (l <= r)
        {
            mid = (l + r) >> 1;
            if (v[mid] <= x)
            {
                rec = mid;
                l = mid + 1;
            }
            else
            {
                r = mid - 1;
            }
        }
        return rec;
    }
    inline void push_back(const int &x)
    {
        v.push_back(x);
    }
} v1[MAXN][MAXN], v2[10005];
int a[100005];
int main()
{
#ifdef lky233
    freopen("testdata.in", "r", stdin);
    freopen("testdata.out", "w", stdout);
#endif
    poread(n);
    poread(m);
    for (register int i = 1; i <= n; ++i)
        poread(a[i]);
    for (register int i = 1; i <= n; ++i)
        v2[a[i]].push_back(i);
    for (register int i = 1; i <= 100; ++i)
    {
        for (register int j = 1; j <= n; ++j)
        {
            v1[i][a[j] % i].push_back(j);
        }
    }
    for (register int i = 1, l, r, p, v; i <= m; ++i)
    {
        poread(l), poread(r), poread(p), poread(v);
        if (p <= 100)
        {
            int ans1, ans2;
            ans1 = ans2 = -1;
            v1[p][v].find(l, ans1);
            if (ans1 == -1)
            {
                puts("0");
                continue;
            }
            v1[p][v].finds(r, ans2);
            if (ans2 == -1)
            {
                puts("0");
                continue;
            }
            printf("%d\n", ans2 - ans1 + 1);
        }
        else
        {
            int pos = v, tot = 0;
            while (pos <= 10000)
            {
                int ans1 = -1, ans2 = -1;
                v2[pos].find(l, ans1);
                // cerr << i << " " << ans1 << " ";
                if (ans1 == -1)
                {
                    pos += p;
                    continue;
                }
                v2[pos].finds(r, ans2);
                // cerr << ans2 << endl;
                if (ans2 == -1)
                {
                    pos += p;
                    continue;
                }
                tot += (ans2 - ans1 + 1);
                pos += p;
            }
            printf("%d\n", tot);
        }
    }
    return 0;
}

这个是\({O(n\sqrt{n})}\)离线的

#include <bits/stdc++.h>
using namespace std;
namespace fdata
{
inline char nextchar()
{
    static const int BS = 1 << 21;
    static char buf[BS], *st, *ed;
    if (st == ed)
        ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? -1 : *st++;
}
#ifdef lky233
#define nextchar getchar
#endif
template <typename T>
inline T poread()
{
    T ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;

    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
    return ret;
}
template <typename Y>
inline void poread(Y &ret)
{
    ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;

    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
const int MAXN = 1e5 + 5;
int n, m;
int a[MAXN];
int c[105][105];
int cc[10005];
struct node
{
    int p, v, id;
    bool op;
    node() {}
    node(int _p, int _v, int _id, int _op) { p = _p, v = _v, id = _id, op = _op; }
} que[MAXN];
int nxt[MAXN << 1], head[MAXN << 1], tot;
int ans[MAXN][2];
int main()
{
#ifdef lky233
    freopen("testdata.in", "r", stdin);
    freopen("testdata.out", "w", stdout);
#endif
    poread(n), poread(m);
    for (register int i = 1; i <= n; ++i)
        poread(a[i]);
    for (register int i = 1, l, r, p, v; i <= m; ++i)
    {
        poread(l), poread(r), poread(p), poread(v);
        que[++tot] = node(p, v, i, 0);
        nxt[tot] = head[l - 1];
        head[l - 1] = tot;
        que[++tot] = node(p, v, i, 1);
        nxt[tot] = head[r];
        head[r] = tot;
    }
    for (register int I = 1; I <= n; ++I)
    {
        ++cc[a[I]];
        for (register int j = 1; j <= 100; ++j)
            ++c[j][a[I] % j];
        for (register int i = head[I]; i; i = nxt[i])
        {
            register int p = que[i].p, v = que[i].v;
            if (p <= 100)
            {
                ans[que[i].id][que[i].op] = c[p][v];
            }
            else
            {
                register int tmp = 0;
                for (register int j = v; j <= 10000; j += p)
                    tmp += cc[j];
                ans[que[i].id][que[i].op] = tmp;
            }
        }
    }
    for (register int i = 1; i <= m; ++i)
        printf("%d", ans[i][1] - ans[i][0]), putchar('\n');
}

附赠随机数据生成器。。。对拍好久。。。

#include <bits/stdc++.h>
using namespace std;
int seed;
int main()
{
    freopen("testdata.out", "r", stdin);
    cin >> seed;
    srand(seed + time(0));
    freopen("testdata.in", "w", stdout);
    int n = (rand() - 1) % 20 + 1;
    int m = (rand() - 1) % 10 + 1;
    cout << n << " " << m << endl;
    for (register int i = 1; i <= n; ++i)
    {
        cout << ((rand() - 1) % 100 + 1) << " ";
    }
    cout << endl;
    for (register int i = 1; i <= m; ++i)
    {
        int l = (rand() - 1) % n + 1;
        int r = (rand() - 1) % n + 1;
        int p = (rand() - 1) % (200) + 1;
        int v = (rand() - 1) % n + 1;
        if (l > r)
            swap(l, r);
        cout << l << " " << r << " " << p << " " << v << endl;
    }
}
posted @ 2019-09-20 17:35  椎名·六花  阅读(250)  评论(0编辑  收藏  举报