NOIP模拟赛[补档]

图论: 差分约束, 2 SAT
数据结构
字符串
数学: FFT / NTT / 线代
DP
计算几何

暴力
线性基 CF 724G

计划:
D1 T1: 斜率优化DP
D1 T2: 差分约束
D1 T3: 数据结构 + 字符串
D2 T1: FFT + DP
D2 T2: 计算几何
D2 T3: 莫比乌斯反演

数据生成(data.c/cpp/pas)

Time Limit: 3 seconds
Memory Limit: 256 megabytes

Description

现有一道题, 我们要给它出数据.
它的输入格式是这样的: 给定一个单调递增的序列\(a_1 < a_2 < ... < a_n\), 满足\(n \le max_n\)\(a_n \le max_a\).
这道题的解法是: 找到\(a_1\)\(a_n\)中所有数的最大公约数\(d\), 假如\(\frac{a_n}d - n\)为偶数, 则输出"Bob"; 否则输出"Alice".
然而, 我们发现这道题目非常容易让不正确的程序水过, 因此我们希望生成一些数据, 能让下列的两种错误代码都输出错误答案:

  • 用于判断奇偶性的数是\(\frac{a_n} d\)
  • 用于判断奇偶性的数是\(a_n - n\)

请你计算出在给定范围内可以产生的符合要求的数据组数. 由于这个数可能很大, 请输出这个数模\(q\)的余数.

Input

一行, 三个数: \(max_n\), \(max_a\), \(q\)

Output

一行答案.

Sample Input

3 6 1000

Sample Output

4

Hint

数据范围:
$
30 %: \
max_n, max_a \le 100 \
100 %: \
1 \le max_n \le 30000 \
max_n \le max_a \le 10^9 \
10^4 \le q \le 10^5 + 126
$

题解

花絮: 题目描述中提及的那道题是Codeforces Round #201A
经过简单的推导, 我们发现, 对于一个符合要求的输入数据, 必须满足以下条件:

  • \(n\)为奇数
  • \(a_n\)为偶数
  • \(\frac{a_n}d\)为奇数

我们对最后一个结论进一步推导:
我们要选出的所有数都应是\(2^k\)的倍数, 并且使得\(a_n\)不是\(2^{k + 1}\)的倍数.
这等效于选出\(1 \le a_i \le \lfloor \frac{max_a}{2^k} \rfloor, \space 1 \le i \le n\)\(a_n\)为奇数.
我们考虑用\(f(b, n, p)\)来表示, 在\([1, b]\)中挑选\(n\)个整数, 并且最后一个的奇偶性为\(p\)的方案数, 并设定边界: \(f(1, 1, 1) = 1\), 同时将\(n = 0\)的值设成\(0\), 以方便后续处理.
则我们有了如下递推式:
\[ f(2b, n, p) = f(b, n, p) + f(b, n, p \oplus(b \& 1)) + \sum_{k = 0}^n (f(b, k, 0) + f(b, k, 1))f(b, n - k, p \oplus(b\& 1)) \]
对于已知所有\(f(b, n, p)\), 要求所有\(f(2b, n, p)\)的情况, 使用这个递归式的复杂度为\(O(n^2)\). 是否有优化的方法呢?
我们令\(x_i = f(b, n, 0) + f(b, n, 1)\), \(y_i = f(b, j, p \oplus (b \& 1))\)
则有:
\[ f(2b, n, p) = f(b, n, p) + f(b, n, p \oplus(b \& 1)) + \sum_{k = 0}^n x_k y_{n - k} \]
我们注意到\(\sum x_k y_{n - k}\)是卷积的形式, 因此我们考虑用FFT处理.
我们又发现已知所有\(f(b, n, p)\)的情况下, 求所有\(f(b + 1, n, p)\)的时间复杂度为\(O(n)\), 因此, 我们要得到任意\(f(b, n, p)\)的时间复杂度都不会超过\(O(max_n \log max_a \log b)\).
最后我们对于每一个\(2^k\), 统计\(\sum_{j = 1}^n f(\lfloor \frac{max_a}{2^k}, j, 1 \rfloor)\)即可.
计算所有\(\frac{a_n}{2^k}\)合在一起算, 需要计算\(\log n\)次, 因此正到题目的时间复杂度为\(O(max_n \log max_n \log max_a)\).

数据

存在\(max_a = 1\)的点, 需要特判. FFT可能要用long double.

标程

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>

using namespace std;
const int N = (int)3e4; 
int n, a, q;
namespace convolution
{
    int rev[N << 2], len;
    inline int initialize(int n)
    {
        len = 1;
        int tmp = 0;
        for(; len < n << 1; ++ tmp, len <<= 1);
        rev[0] = 0;
        for(int i = 1; i < len; ++ i)
            rev[i] = rev[i >> 1] >> 1 | (i & 1) << tmp - 1;
    }
    struct complex
    {
        long double rl, img;
        inline complex() {}
        inline complex(long double _rl, long double _img)
        {
            rl = _rl, img = _img;
        }
        inline complex friend operator +(complex a, complex b)
        {
            return complex(a.rl + b.rl, a.img + b.img);
        }
        inline complex friend operator -(complex a, complex b)
        {
            return complex(a.rl - b.rl, a.img - b.img);
        }
        inline complex friend operator *(complex a, complex b)
        {
            return complex(a.rl * b.rl - a.img * b.img, a.rl * b.img + b.rl * a.img);
        }
    }A[N << 2], B[N << 2];
    long double PI = acos(-1);
    inline void FFT(complex *a, int opt)
    {
        for(int i = 0; i < len; ++ i)
            if(rev[i] < i)
                std::swap(a[i], a[rev[i]]);
        for(int i = 2; i <= len; i <<= 1)
        {
            complex omega_i = complex(cos(2 * PI * opt / i), sin(2 * PI * opt / i));
            for(int j = 0; j < len; j += i)
            {
                complex omega = complex(1, 0);
                for(int k = j; k < j + i / 2; ++ k)
                {
                    complex u = a[k], t = a[k + i / 2] * omega;
                    a[k] = u + t, a[k + i / 2] = u - t;
                    omega = omega * omega_i; 
                }
            }
        }
        if(opt == -1)
            for(int i = 0; i < len; ++ i)
                a[i].rl /= len;
    }
    inline void work(int *a, int *b, int n, long long *res)
    {
        memset(A, 0, sizeof(A)), memset(B, 0, sizeof(B));
        for(int i = 0; i < n; ++ i)
            A[i] = complex(a[i], 0), B[i] = complex(b[i], 0);
        FFT(A, 1), FFT(B, 1);
        for(int i = 0; i < len; ++ i)
            A[i] = A[i] * B[i];
        FFT(A, -1);
        for(int i = 0; i < len; ++ i)
            res[i] = (long long)(A[i].rl + 0.5);
    }
}
int f[N + 1][2], _f[N + 1][2];
int ans;
inline void update()
{
    for(int i = 1; i <= n; i += 2)
        ans = (ans + f[i][1]) % q;
}
void work(int b)
{
    if(b == 1)
    {
        memset(f, 0, sizeof(f));
        f[1][1] = 1;
        update();
        return;
    }
    work(b / 2);
    static int x[N + 1], y[N + 1];
    std::swap(f, _f);
    for(int i = 0; i <= n; ++ i)
        x[i] = (_f[i][0] + _f[i][1]) % q;
    for(int i = 0; i < 2; ++ i)
    {
        for(int j = 0; j <= n; ++ j)
            y[j] = _f[j][i ^ (b >> 1 & 1)];
        static long long res[N << 2];
        convolution::work(x, y, n + 1, res);
        for(int j = 0; j <= n; ++ j)
            f[j][i] = (res[j] + _f[j][i] + _f[j][i ^ (b >> 1 & 1)]) % q;
    }
    if(b & 1)
    {
        std::swap(f, _f);
        for(int i = 0; i < 2; ++ i)
        {
            f[0][i] = _f[0][i];
            for(int j = 1; j <= n; ++ j)
                f[j][i] = (_f[j][i] + (i ^ (b & 1) ? 0 : (j == 1 ? 1 : _f[j - 1][0] + _f[j - 1][1]))) % q;
        }
    }
    update();
}
int main()
{

    #ifndef ONLINE_JUDGE
    freopen("CF773F.in", "r", stdin);
//  freopen("CF773F.out", "w", stdout);
    #endif

    using namespace std;
    cin >> n >> a >> q;
    if(a == 1)
    {
        puts("0");
        return 0;
    }
    convolution::initialize(n + 1);
    ans = 0;
    work(a / 2);
    cout << ans << endl;
}

说唱天王(rap.c/cpp/pas)

Memory limit: 256 megabytes
Time limit: 2 seconds

Description

一个二货号称自己是说唱天王.
我们甭管他是真的说唱天王, 还是假的说唱天王, 反正他要你协助他作曲. 我们也甭管他是怎么作曲的, 总之你的任务是这样的: 给定一棵树, 每条树边都代表一个字母. 我们用一个有序整数对\((u, v)\), 表示从编号为\(u\)的节点到编号为\(v\)的节点的最短路径上的边组成的字符串.
对于每个询问, 给定一个有序整数对\((u, v )\), 请你输出, 在整棵树上可以找到多少个\(w\), 使得\((u, w) < (u, v)\), 也就是从\(u\)\(w\)组成的字符串的字典序小于从\(u\)\(v\)组成的字符串.

Input

第一行两个数\(n\)\(q\), 表示树的点数和询问个数
接下来的\(n - 1\)行, 每行表示树上的一条边, 用两个整数\(u\)\(v\)和一个字符\(c\)表示, 表示编号为\(u\)的点与编号为\(v\)的点之间有一条连边, 其对应的字母为\(c\).
接下来\(q\)行, 每行两个整数\(u\), \(v\), 表示询问中的有序整数对\((u, v)\).

Output

\(q\)行.
对于每个询问, 输出一个整数, 即答案.

Sample 1

input:

4 3
4 1 t
3 2 p
1 2 s
3 2
1 3
2 1

output:

0
1
1

Sample 2

input:

8 4
4 6 p
3 7 o
7 8 p
4 5 d
1 3 o
4 3 p
3 2 e
8 6
3 7
8 1
4 3

output:

6
1
3
1

Hint

数据范围:
\(n, q \le 20000\)
\(1 \le u, v \le n\)
\(c\)为小写字母

题解

树分治.
对于每一次分治, 我们从分治重心开始DFS, 建立一棵trie树. 先序遍历这一棵trie树, 得到一个DFS序. 用树状数组维护即可.
详细的题解看这里:
http://codeforces.com/blog/entry/51163

代码

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#include <map>

namespace Zeonfai
{
    inline int getInt()
    {
        int a = 0, sgn = 1;
        char c;
        while(! isdigit(c = getchar()))
            if(c == '-')
                sgn *= -1;
        while(isdigit(c))
            a = a * 10 + c - '0', c = getchar();
        return a * sgn; 
    }
    inline char getChar()
    {
        char c;
        while(! isalpha(c = getchar()));
        return c;
    }
    inline void print(int a)
    {
        if(! a)
            return;
        print(a / 10);
        putchar('0' + a % 10);
    } 
    inline void println(int a)
    {
        if(a < 0)
            putchar('-'), a *= -1;
        if(a == 0)
            putchar('0');
        print(a);
        putchar('\n');
    }
}
const int N = (int)2e4, K = 47, LOG = 15, MOD = 998244353, Q = (int)2e4;
int n, q;
int pw[N], pwInv[N];
inline int getInverse(int a)
{
    int res = 1;
    for(int i = MOD - 2; i; a = (long long)a * a % MOD, i >>= 1)
        if(i & 1)
            res = (long long)res * a % MOD;
    return res;
}
int anc[N + 1][LOG], up[N + 1], dwn[N + 1], dep[N + 1];
struct query
{
    int u, v, LCA, ans;
    inline query()
    {
        ans = 0;
    }
}qry[Q];
inline int getLCA(int id)
{
    int u = qry[id].u, v = qry[id].v;
    if(dep[u] < dep[v])
        std::swap(u, v);
    for(int i = LOG - 1; ~ i; -- i)
        if(dep[u] - (1 << i) >= dep[v])
            u = anc[u][i];
    if(u == v)
        return u;
    for(int i = LOG - 1; ~ i; -- i)
        if(anc[u][i] ^ anc[v][i])
            u = anc[u][i], v = anc[v][i];
    return anc[u][0];
}
struct binaryIndexedTree
{
    int a[N + 1];
    inline void build(int bnd)
    {
        for(int i = 1; i <= bnd; ++ i)
            if(i + (i & - i) <= bnd)
                a[i + (i & - i)] += a[i];
    }
    inline void modify(int pos, int x, int bnd)
    {
        for(int i = pos; i <= bnd; i += i & - i)
            a[i] += x;
    }
    inline int query(int pos)
    {
        if(pos <= 0)
            return 0;
        int res = 0;
        for(int i = pos; i; i -= i & - i)
            res += a[i];
        return res;
    }
}BIT;
struct trieTree
{
    struct node
    {
        node *suc[27];
        int cnt, dfn, ed;
        inline node()
        {
            for(int i = 1; i <= 26; ++ i)
                suc[i] = NULL;
            cnt = 0;
        }
    }*rt;
    void clear(node *u)
    {
        for(int i = 1; i <= 26; ++ i)
            if(u->suc[i] != NULL)
                clear(u->suc[i]);
        delete u;
    }
    inline clear()
    {
        if(rt != NULL)
            clear(rt);
        rt = new node;
    }
    int clk;
    void DFS(node *u)
    {
        u->ed = u->dfn = ++ clk;
        BIT.a[u->dfn] = u->cnt;
        for(int i = 1; i <= 26; ++ i)
            if(u->suc[i] != NULL)
                DFS(u->suc[i]), u->ed = u->suc[i]->ed;
    }
    inline void DFS()
    {
        clk = 0;
        DFS(rt);
        BIT.build(clk);
    }
}trie;
struct tree
{
    struct node;
    struct edge
    {
        node *v;
        int c;
        inline edge(node *_v, int _c)
        {
            v = _v, c = _c;
        }
    };
    struct node
    {
        std::vector<edge> edg;
        std::vector<int> qry;
        int vst, sz, mx;
        int up, dwn, dep;
        node *anc[LOG];
        trieTree::node *pos;
        inline node()
        {
            edg.clear(), qry.clear(), vst = 0;
        } 
    }nd[N + 1];
    inline void addEdge(int u, int v, char c)
    {
        nd[u].edg.push_back(edge(nd + v, c - 'a' + 1)), nd[v].edg.push_back(edge(nd + u, c - 'a' + 1));
    }
    void DFS(int u, int pre, int c)
    {
        dep[u] = dep[pre] + 1;
        up[u] = ((long long)up[pre] * K + c) % MOD, dwn[u] = (dwn[pre] + (long long)c * pw[dep[u] - 1]) % MOD;
        anc[u][0] = pre;
        for(int i = 1; i < LOG; ++ i)
            anc[u][i] = anc[anc[u][i - 1]][i - 1];
        for(auto edg : nd[u].edg)
            if(edg.v - nd != pre)
                DFS(edg.v - nd, u, edg.c);
    }
    inline void getDoublingTable()
    {
        up[1] = dwn[1] = 0;
        dep[1] = -1;
        DFS(1, 1, 0);
    }
    void getSize(node *u, node *pre)
    {
        u->sz = 1;
        for(auto edg : u->edg)
            if(! edg.v->vst && edg.v != pre)
                getSize(edg.v, u), u->sz += edg.v->sz;
    }
    node* getRoot(node *u, node *pre, node *tp)
    {
        u->mx = tp->sz - u->sz;
        for(auto edg : u->edg)
            if(! edg.v->vst && edg.v != pre)
                u->mx = std::max(u->mx, edg.v->sz);
        node *res = u;
        for(auto edg : u->edg)
            if(! edg.v->vst && edg.v != pre)
            {
                node *tmp = getRoot(edg.v, u, tp);
                if(tmp->mx < res->mx)
                    res = tmp;
            }
        return res;
    }
    std::map<int, trieTree::node*> mp;
    void DFS(node *u, node *pre, int c)
    {
        u->dep = pre->dep + 1;
        u->up = ((long long)pre->up * K + c) % MOD;
        u->anc[0] = pre;
        for(int i = 1; i < LOG; ++ i)
            u->anc[i] = u->anc[i - 1]->anc[i - 1];
        u->pos = (pre->pos->suc[c] == NULL ? pre->pos->suc[c] = new trieTree::node : pre->pos->suc[c]);
        ++ u->pos->cnt;
        mp[u->dwn = (pre->dwn + (long long)c * pw[u->dep - 1]) % MOD] = u->pos;
        u->sz = 1;
        for(auto edg : u->edg)
            if(! edg.v->vst && edg.v != pre)
                DFS(edg.v, u, edg.c), u->sz += edg.v->sz;
    }
    void modify(node *u, node *pre, int x)
    {
        BIT.modify(u->pos->dfn, x, trie.clk);
        for(auto edg : u->edg)
            if(! edg.v->vst && edg.v != pre)
                modify(edg.v, u, x);
    }
    inline int getHash(int id, int L, int R)
    {
        if(R > dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1))
            return -1;
        int u, v;
        if(R <= dep[qry[id].u] - dep[qry[id].LCA])
        {
            u = qry[id].u;
            for(int i = LOG - 1; ~ i; -- i)
                if(1 << i <= L)
                    u = anc[u][i], L -= 1 << i;
            v = qry[id].u;
            for(int i = LOG - 1; ~ i; -- i)
                if(1 << i <= R)
                    v = anc[v][i], R -= 1 << i;
            return (up[u] - (long long)up[v] * pw[dep[u] - dep[v]] % MOD + MOD) % MOD;
        }
        else if(L <= dep[qry[id].u] - dep[qry[id].LCA] && R > dep[qry[id].u] - dep[qry[id].LCA])
        {
            u = qry[id].u;
            for(int i = LOG - 1; ~ i; -- i)
                if(1 << i <= L)
                    u = anc[u][i], L -= 1 << i;
            v = qry[id].v, R = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - R;
            for(int i = LOG - 1; ~ i; -- i)
                if(1 << i <= R)
                    v = anc[v][i], R -= 1 << i;
            return (up[u] - (long long)up[qry[id].LCA] * pw[dep[u] - dep[qry[id].LCA]] % MOD + MOD 
            + (long long)(dwn[v] - dwn[qry[id].LCA] + MOD) * pwInv[dep[qry[id].LCA]] % MOD * pw[dep[u] - dep[qry[id].LCA]] % MOD) % MOD;
        }
        else if(L > dep[qry[id].u] - dep[qry[id].LCA])
        {
            u = qry[id].v, L = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - L;
            for(int i = LOG - 1; ~ i; -- i)
                if(1 << i <= L)
                    u = anc[u][i], L -= 1 << i;
            v = qry[id].v, R = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - R;
            for(int i = LOG - 1; ~ i; -- i)
                if(1 << i <= R)
                    v = anc[v][i], R -= 1 << i;
            return (long long)(dwn[v] - dwn[u] + MOD) * pwInv[dep[u]] % MOD;
        }
    }
    node* cen;
    int curSz;
    void update(int id)
    {
        node *u = nd + qry[id].u;
        int len = 0;
        for(int i = LOG - 1; ~ i; -- i)
            if(len + (1 << i) <= dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1))
                if(1 << i <= u->dep && getHash(id, len, len + (1 << i)) == (u->up - (long long)u->anc[i]->up * pw[1 << i] % MOD + MOD) % MOD)
                    u = u->anc[i], len += 1 << i;
        if(u != cen)
        {
            if((u->up - (long long)u->anc[0]->up * K % MOD + MOD) % MOD < getHash(id, len, len + 1))
                qry[id].ans += cen->sz - curSz;
            return;
        }
        int L = 0, R = dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1) - len;
        int res;
        while(L <= R)
        {
            int mid = L + R >> 1;
            int hsh = getHash(id, len, len + mid);
            if(mp.find(hsh) != mp.end())
                L = mid + 1, res = mid;
            else
                R = mid - 1;
        }
        if(len + res == dep[qry[id].u] + dep[qry[id].v] - (dep[qry[id].LCA] << 1))
            qry[id].ans += BIT.query(mp[getHash(id, len, len + res)]->dfn - 1);
        else
        {
            trieTree::node *u = mp[getHash(id, len, len + res)];
            int c = getHash(id, len + res, len + res + 1);
            trieTree::node *p = NULL;
            for(int i = 1; i < c; ++ i)
                if(u->suc[i] != NULL)
                    p = u->suc[i];
            if(p == NULL)
                qry[id].ans += BIT.query(mp[getHash(id, len, len + res)]->dfn);
            else
                qry[id].ans += BIT.query(p->ed);
        }
    } 
    void getAnswer(node *u, node *pre)
    {
        for(auto id : u->qry)
            update(id);
        for(auto edg : u->edg)
            if(! edg.v->vst && edg.v != pre)
                getAnswer(edg.v, u);
    }
    void work(node *u)
    {
        getSize(u, u);
        cen = u = getRoot(u, u, u);
        mp.clear();
        trie.clear();
        u->pos = trie.rt;
        ++ u->pos->cnt;
        u->dep = u->up = u->dwn = 0;
        mp[u->dwn] = u->pos;
        for(int i = 0; i < LOG; ++ i)
            u->anc[i] = u;
        u->sz = 1;
        for(auto edg : u->edg)
            if(! edg.v->vst)
                DFS(edg.v, u, edg.c), u->sz += edg.v->sz;
        trie.DFS();
        BIT.modify(u->pos->dfn, -1, trie.clk);
        for(auto id : u->qry)
            update(id);
        BIT.modify(u->pos->dfn, 1, trie.clk);
        for(auto edg : u->edg)
            if(! edg.v->vst)
            {
                curSz = edg.v->sz;
                modify(edg.v, u, -1);
                getAnswer(edg.v, u);
                modify(edg.v, u, 1);
            }
        u->vst = 1;
        for(auto edg : u->edg)
            if(! edg.v->vst)
                work(edg.v);
    } 
    inline void decomposition()
    {
        work(nd + 1);
    }
}T;
int main()
{
    
    #ifndef ONLINE_JUDGE
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    #endif
    
    using namespace Zeonfai;
    n = getInt(), q = getInt();
    for(int i = 1; i < n; ++ i)
    {
        int u = getInt(), v = getInt();
        char c = getChar();
        T.addEdge(u, v, c);
    }
    pw[0] = pwInv[0] = 1;
    for(int i = 1; i < n; ++ i)
         pw[i] = (long long)pw[i - 1] * K % MOD, pwInv[i] = getInverse(pw[i]);
    T.getDoublingTable();
    for(int i = 0; i < q; ++ i)
        T.nd[qry[i].u = getInt()].qry.push_back(i), qry[i].v = getInt(), qry[i].LCA = getLCA(i);
    T.decomposition();
    for(int i = 0; i < q; ++ i)
        println(qry[i].ans);
}

关卡(game.c/cpp/pas)

Time Limit: 1 second
Memory Limit: 256 megabytes

Description

现有这样一个游戏: 这个游戏有\(n\)个关卡, 每个关卡有两个属性: 一个整数\(t_i\), 用于随机挑选关卡, 以及一个boolean型\(tag\), 表示这个关卡是否已经被挑战成功. 你要将这些关卡分为\(k\)个连续的段, 每一段称为一组. 根据游戏的设定, 开始时, 我们把每个关卡的\(tag\)设定为\(false\), 也就是未完成, 并且读入每一个\(t_i\). 每次进行游戏时, 我们随机找到任意一个存在未完成关卡的组\(X\), 它将会在\(X\)中通过某种方式选出一个关卡让你挑战. 具体来说, 它会计算\(X\)中每个\(tag\)\(true\)的关卡的\(t_i\)之和\(sum\), 同时找到\(X\)中从左起第一个\(tag\)\(false\)的关卡\(p\), 将\(sum\)加上\(t_p\), 然后在所有\(tag\)\(true\)的关卡以及\(p\)中, 每个关卡被选出来让你挑战的机率为\(\frac{t_i}{sum}\). 一个关卡只要被选出来让你挑战, 你就必须接受挑战, 无论之前你是否已经将其挑战成功过. 假如你将一个原本未完成的关卡挑战成功, 则这个关卡的\(tag\)会变成\(true\).
现在, 我们假设你的水平高超, 挑战任意一个关卡都必定能成功, 并且需要花费一个单位的时间. 那么, 我们希望知道, 通过合理地对所有关卡进行分组, 你完成所有关卡(也就是将所有关卡的\(tag\)变为\(true\))的期望时间最小是多少?

Input

两行.
第一行: 两个数, \(n\)表示有\(n\)个关卡, \(k\)表示要分成\(k\)组.
第二行: \(n\)个数, 分别为\(t_1\)\(t_n\)

Output

一个浮点数, 精确到小数点后四位.

Sample 1

Input
4 2
100 3 5 7
Output
5.7429

Sample 2

Input
6 2
1 2 4 8 16 32
Output
8.5000

Hint

$
1 \le n \le 2 \times 10^5 \
1 \le k \le \min(n, 50) \
1 \le t_i \le 10^5
$

Solution

斜率优化.
略.

Code

#include <cstdio>
#include <cctype>
#include <algorithm>

namespace Zeonfai
{
    inline int getInt()
    {
        int a = 0, sgn = 1;
        char c;
        while(! isdigit(c = getchar()))
            if(c == '-')
                sgn *= -1;
        while(isdigit(c))
            a = a * 10 + c - '0', c = getchar();
        return a * sgn;
    }
}
const int N = (int)2e5;
const double INF = 1e30;
static double sum[N + 1], a[N + 1], b[N + 1], f[N + 1], _f[N + 1];
inline double slope(int i, int j)
{
    return ((_f[i] - a[i] + sum[i] * b[i]) - (_f[j] - a[j] + sum[j] * b[j])) / (sum[i] - sum[j]);
}
int main()
{
    
    #ifndef ONLINE_JUDGE
    freopen("CF674C.in", "r", stdin);
    #endif
    
    using namespace Zeonfai;
    int n = getInt(), k = getInt();
    static int t[N + 1];
    for(int i = 1; i <= n; ++ i)
        t[i] = getInt();
    a[0] = b[0] = sum[0] = 0;
    for(int i = 1; i <= n; ++ i)
        sum[i] = sum[i - 1] + t[i], a[i] = a[i - 1] + sum[i] / t[i], b[i] = b[i - 1] + (double)1 / t[i];
    for(int i = 0; i <= n; ++ i)
        f[i] = a[i];
    for(int i = 1; i < k; ++ i)
    {
        std::swap(f, _f);
        static int que[N + 1];
        int hd = 0, tl = 0;
        for(int j = 0; j <= n; ++ j)
        {
            while(hd + 1 < tl && slope(que[hd + 1], que[hd]) < b[j])
                ++ hd;
            if(tl > hd)
            {
                int k = que[hd];
                f[j] = _f[k] + a[j] - a[k] - sum[k] * (b[j] - b[k]);
            }
            else
                f[j] = INF;
            while(hd + 1 < tl && slope(j, que[tl - 1]) < slope(que[tl - 1], que[tl - 2]))
                tl --;
            que[tl ++] = j;
        }
    }
    printf("%.4lf", f[n]);
}

雨水收集器(rain.c/cpp/pas)

Memory limit: 128 megabytes
Time limit: 2 seconds

Description

研究二维世界中的事情总是非常有趣的.
现有这样一个二维世界, 雨水源源不断地从天空中竖直降下来. 我们用一个由两条线段组成的容器来接收雨水, 问最多可以接到多少雨水.
我们给出每一条线段的两个端点坐标, 请你计算出答案.
注意: 不保证两条线段相交.

rain

Input

一个整数\(n\), 表示有\(n\)组询问
每组询问包含\(8\)个浮点数, 分为两组, 每组表示一条线段两个端点的坐标.

Output

一个浮点数, 保留小数点后\(2\)位, 表示答案.

Sample

input:

3
0 1 1 0
1 0 2 1
0 1 2 1
1 0 1 2
0 0 -0.5 0.5
1 1 2 3

output:

1.00
0.00
0.00

Hint

只有一组数据.
\(n \le 10^5\)
每个坐标的数值\(|p| \le 1000\)

Solution

对于答案不为\(0\)的情况, 我们直接计算, 这里不再赘述.
考虑什么情况下答案为\(0\):

  • 两条线段不相交
  • 靠上的一条线段完全覆盖下面的线段(也就是雨水进不去的情况)

其中第二种情况比较难考虑到.

Code

#include <cstdio>
#include <algorithm>
#include <cstdlib>

const double INF = 1e50;
const double EPS = 1e-8;
struct coordinate
{
    double x, y;
    inline coordinate() {}
    inline coordinate(double _x, double _y)
    {
        x = _x, y = _y;
    }
    inline coordinate friend operator -(const coordinate &a, const coordinate &b)
    {
        return coordinate(a.x - b.x, a.y - b.y);
    }
    inline double friend operator ^(const coordinate &a, const coordinate &b)
    {
        return a.x * b.y - a.y * b.x;
    }
};
struct line
{
    coordinate p, q;
    double k, b;
};
int flg;
inline coordinate cross(line a, line b)
{
    if(((a.q - a.p) ^ (b.p - a.p)) * ((a.q - a.p) ^ (b.q - a.p)) > 0 || ((b.q - b.p) ^ (a.p - b.p)) * ((b.q - b.p) ^ (a.q - b.p)) > 0 || a.k == b.k)
    {
        puts("0.00");
        flg = 1;
        return coordinate();
    }
    coordinate res;
    if(a.k != INF && b.k != INF)
        res.x = (b.b - a.b) / (a.k - b.k), res.y = a.k * res.x + a.b;
    else
    {
        if(a.k == INF)
            std::swap(a, b);
        res = coordinate(b.p.x, a.k * b.p.x + a.b);
    }
    return res;
}
int main()
{

    #ifndef ONLINE_JUDGE
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    #endif

    int n;
    for(scanf("%d", &n); n --;)
    {
        line a, b;
        scanf("%lf%lf%lf%lf", &a.p.x, &a.p.y, &a.q.x, &a.q.y);
        a.k = a.p.x == a.q.x ? INF : (a.q.y - a.p.y) / (a.q.x - a.p.x), a.b = a.p.y - a.p.x * a.k;
        scanf("%lf%lf%lf%lf", &b.p.x, &b.p.y, &b.q.x, &b.q.y);
        b.k = b.p.x == b.q.x ? INF : (b.q.y - b.p.y) / (b.q.x - b.p.x), b.b = b.p.y - b.p.x * b.k;
        flg = 0;
        coordinate crs = cross(a, b);
        if(flg)
            continue;
        if(a.p.y < a.q.y)
            std::swap(a.p, a.q);
        if(b.p.y < b.q.y)
            std::swap(b.p, b.q);
        if((a.p.x < crs.x) == (b.p.x < crs.x) && (a.p.x < crs.x && (a.p.x < b.p.x) == (a.k < b.k) || a.p.x > crs.x && (a.p.x > b.p.x) == (a.k > b.k)))
        {
            puts("0.00");
            continue;
        }
        if(a.p.y < b.p.y)
            std::swap(a, b);
        a.p.y = b.p.y, a.p.x = a.k == INF ? a.p.x : (a.p.y - a.b) / a.k;
        double ans = ((a.p - crs) ^ (b.p - crs)) / 2;
        printf("%.2lf\n", (ans < 0 ? - ans : ans) + EPS);
    }
}

转载于:https://www.cnblogs.com/ZeonfaiHo/p/9740025.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值