HNOI2011解题报告

8 篇文章 0 订阅
6 篇文章 0 订阅

HNOI2011解题报告

Author: Pengyihao

Day1 T1 数学作业


题意

给出正整数 n , m,要求将 1n n 个数连接起来,问连接起来的数对 m 取模的结果是多少。

1n1018,1m109

思路

如果连接的数的位数一样,那么可以用矩阵乘法进行优化。因为有

nowans=lastans10k+now

这个恒定的递推式(对于位数一样的数),所以可以分段矩阵乘法。

因为只有 lg(n) 个位数,所以可以解决这个问题。

代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (LL i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define REP(i, a, b) for (LL i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}

template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}

LL n; LL m;

LL ten[20];
LL tmpans;

LL ret[4][4], tmp[4][4];

void mul(LL x[4][4], LL y[4][4], LL z[4][4])
{
    LL t[4][4];
    memset(t, 0, sizeof t);
    FOR(i, 1, 3) FOR(j, 1, 3) FOR(k, 1, 3)
        t[i][j] = (t[i][j] + 1ll * x[i][k] * y[k][j] % m) % m;
    memcpy(z, t, sizeof t);
}

bool work(LL x)
{
    bool flagend = false;
    LL ci = ten[x] - ten[x - 1], fr = ten[x - 1] % m;

    if (n < ten[x]) {
        ci = n - ten[x - 1] + 1;
        flagend = true;
    }

    memset(ret, 0, sizeof ret);
    FOR(i, 1, 3) ret[i][i] = 1;

    memset(tmp, 0, sizeof tmp);
    tmp[1][1] = 1; tmp[1][2] = 1; tmp[2][2] = 1;
    tmp[2][3] = 1; tmp[3][3] = ten[x] % m;

    while (ci) {
        if (ci & 1) mul(ret, tmp, ret);
        mul(tmp, tmp, tmp);
        ci >>= 1;
    }

    tmpans = (
        1ll * ret[1][3] % m +
        1ll * fr * ret[2][3] % m +
        1ll * tmpans * ret[3][3] % m
    ) % m;

    if (flagend) {
        printf("%lld\n", tmpans);
        return true;
    }
    return false;
}

int main()
{
    freopen("homework.in", "r", stdin);
    freopen("homework.out", "w", stdout);

    in(n); in(m);
    ten[0] = 1;
    FOR(i, 1, 18) ten[i] = ten[i - 1] * 10;

    FOR(i, 1, 18) if (work(i)) break;
    return 0;
}

Day1 T2 勾股定理


这个题目有点问题,我在网上看题解的时候,发现这题的做法其实是不靠谱的,意思是说这是一道玄学的题目。

对于题目所给的数据范围,标程不一定都能在合理的时间内跑出答案。

所以我就跳过了这道题目。


Day1 T3 赛车游戏


思路

首先可以用拉格朗日乘数法证明,如果要达到最优成绩,那么每条路上的速度要尽可能相等。

于是就可以二分这个速度,然后计算耗油量。

注意如果某条路上耗油量为负数,那么就不能在这条路上用当前二分的速度来计算,因为可能耗油量为负数。

所以我们可以把这条路上的速度设置为令耗油量为0的速度。

这样就可以正确地计算耗油量和跑的时间了。

代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}

template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}

const int MAXN = 10010;

int T, n;
double a, b, vmax, f;
double s[MAXN], k[MAXN], sv[MAXN];

const int LIMITS = 1000;

double check(double _v)
{
    double sum = 0;

    FOR(i, 1, n) {
        if (_v < sv[i]) {
            sum = sum + Max(0., a * sv[i] + b * k[i]) * s[i];
        }
        else sum = sum + Max(0., a * _v + b * k[i]) * s[i];
    }

    return sum;
}

int main()
{
    freopen("race.in", "r", stdin);
    freopen("race.out", "w", stdout);

    in(T);

    while (T--) {
        scanf("%lf%lf%lf%lf", &a, &b, &vmax, &f);

        in(n);
        FOR(i, 1, n) {
            double x, y;
            scanf("%lf%lf", &x, &y);
            x /= 1000.0; y /= 1000.0;

            s[i] = sqrt(x * x + y * y);
            k[i] = y / x;

            if (k[i] < 0) sv[i] = Min(-b * k[i] / a, vmax);
            else sv[i] = 0;
        }

        double l = 0, r = 1000000000;

        FOR(i, 1, LIMITS) {
            double mid = (l + r) / 2;
            if (mid > vmax || check(mid) > f) r = mid;
            else l = mid;
        }

        if (check(l) > f) puts("IMPOSSIBLE");
        else {
            double ret = 0;

            FOR(i, 1, n) {
                if (sv[i] > l) ret += s[i] / sv[i];
                else ret += s[i] / l;
            }

            printf("%.5lf\n", ret);
        }
    }

    return 0;
}

Day1 T4 括号修复


思路

首先对于一个括号序列,如何计算它最少需要改多少个括号呢?

我们发现如果把可以匹配的括号一层一层去掉,那么最后一定会变成下面这个样子:

))))))))(((((((((

就是左边一连串的括号,右边一连串的括号。

假设左边有 l 个括号,右边有 r 个括号。

那么一共要改

l+12+r+12

个括号。

根据“维修数列”这一题的经验,我们可以用splay来维护括号序列。

用+1表示’(‘,用-1表示’)’,那么左边的括号数量就是从左开始的最小子段和,右边的括号数量就是从右开始的最大子段和。

操作1:直接打标记。

操作2:直接打标记。

操作3:变换一下从左开始的最小、最大子段和,从右开始的最小、最大子段和。

操作4:直接取值。

怎么合并标记呢?

当打区间赋值标记的时候,可以直接清空反转标记。

当打反转标记的时候,要将赋值标记乘上-1。

代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}

template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}

template <typename Tp> void swap(Tp &x, Tp &y) {Tp z = x; x = y; y = z;}

struct Node {
    bool isa, ist_dn, isset;

    Node *ch[2], *fa;
    int lmax, lmin, rmax, rmin, data, sum, sz, wt_set;

    void update();
    void pushdown();

    void rotate();
    void splay(Node*);
};

const int MAXN = 100010;

Node *nul, *rot, *to[MAXN];

int n, m;
char str[MAXN];

void push(Node *hr, Node *top)
{
    if (hr != nul) push(hr -> fa, top);
    if (hr != nul) hr -> pushdown();
}

void Node::splay(Node *top)
{
    push(this, nul);
    if (ch[0] != nul) ch[0] -> pushdown();
    if (ch[1] != nul) ch[1] -> pushdown();
    while (fa != top) {
        if (fa -> fa != top) {
            bool t = (fa -> fa -> ch[0] == fa ? 0 : 1);
            if (fa -> ch[t] == this) {
                fa -> rotate(); rotate();
            }
            else rotate(), rotate();
        }
        else rotate();
    }
    if (top == nul) rot = this;
}

void Node::rotate()
{
    Node *pa = fa;
    fa = pa -> fa;
    if (fa != nul) {
        bool t = (fa -> ch[0] == pa ? 0 : 1);
        fa -> ch[t] = this;
    }

    pa -> fa = this;

    bool t = (pa -> ch[0] == this ? 0 : 1);

    pa -> ch[t] = ch[t ^ 1];
    if (ch[t ^ 1] != nul) ch[t ^ 1] -> fa = pa;

    ch[t ^ 1] = pa;

    pa -> update(); update();
}

void Node::update()
{
    if (ch[0] != nul) ch[0] -> pushdown();
    if (ch[1] != nul) ch[1] -> pushdown();

    sum = data; sz = 1;
    if (ch[0] != nul) sum += ch[0] -> sum, sz += ch[0] -> sz;
    if (ch[1] != nul) sum += ch[1] -> sum, sz += ch[1] -> sz;

    lmin = Min(0, Min(ch[0] -> lmin, ch[0] -> sum + data + ch[1] -> lmin));
    rmin = Min(0, Min(ch[1] -> rmin, ch[1] -> sum + data + ch[0] -> rmin));

    lmax = Max(0, Max(ch[0] -> lmax, ch[0] -> sum + data + ch[1] -> lmax));
    rmax = Max(0, Max(ch[1] -> rmax, ch[1] -> sum + data + ch[0] -> rmax));
}

void Node::pushdown()
{
    if (ist_dn) {
        ist_dn = false;

        int tlmin = lmin, trmin = rmin;
        int tlmax = lmax, trmax = rmax;

        data = -data; sum = -sum;

        lmin = -tlmax; lmax = -tlmin;
        rmax = -trmin; rmin = -trmax;

        wt_set = -wt_set;

        if (ch[0] != nul) {
            ch[0] -> ist_dn ^= 1;
//            if (ch[0] -> isset)
//                ch[0] -> wt_set = -ch[0] -> wt_set;
        }
        if (ch[1] != nul) {
            ch[1] -> ist_dn ^= 1;
//            if (ch[1] -> isset)
//                ch[1] -> wt_set = -ch[1] -> wt_set;
        }
    }

    if (isset) {
        isset = false;
        if (ch[0] != nul)
            ch[0] -> isset = true, ch[0] -> wt_set = wt_set, ch[0] -> ist_dn = false;
        if (ch[1] != nul)
            ch[1] -> isset = true, ch[1] -> wt_set = wt_set, ch[1] -> ist_dn = false;

        data = wt_set; sum = wt_set * sz;
        lmin = rmin = wt_set < 0 ? wt_set * sz : 0;
        lmax = rmax = wt_set > 0 ? wt_set * sz : 0;
    }

    if (isa) {
        isa = false;
        if (ch[0] != nul) ch[0] -> isa ^= 1;
        if (ch[1] != nul) ch[1] -> isa ^= 1;

        swap(lmin, rmin);
        swap(lmax, rmax);

        swap(ch[0], ch[1]);
    }
}

void start()
{
    nul = new Node;

    nul -> sz = 0;
    nul -> data = nul -> sum = 0;
    nul -> isa = nul -> ist_dn = nul -> isset = false;

    nul -> ch[0] = nul -> ch[1] = nul -> fa = nul;

    nul -> lmax = nul -> rmax = 0;
    nul -> lmin = nul -> rmin = 0;
}

void insert(int now)
{
    to[now] = new Node;
    if (now == 1) rot = to[now];

    to[now] -> sz = 1;
    to[now] -> isa = to[now] -> isset = to[now] -> ist_dn = false;

    to[now] -> ch[0] = to[now] -> ch[1] = to[now] -> fa = nul;
    if (now != 1) to[now] -> fa = to[now - 1], to[now - 1] -> ch[1] = to[now];

    to[now] -> data = (str[now] == '(' ? 1 : -1);
    to[now] -> sum = to[now] -> data;
}

Node *find_key(int rnk)
{
    Node *x = rot;
    while (1) {
        x -> pushdown();
        int rrnk = (x -> ch[0] == nul ? 1 : x -> ch[0] -> sz + 1);
        if (rrnk == rnk)
            return x;
        if (rrnk >  rnk)
            x = x -> ch[0];
        else {
            x = x -> ch[1];
            rnk -= rrnk;
        }
    }
}

char command[20];

int main()
{
    freopen("brackets.in", "r", stdin);
    freopen("brackets.out", "w", stdout);

    in(n); in(m);
    scanf("%s", str + 1);

    start();

    FOR(i, 1, n) insert(i);

    DNF(i, n, 1) to[i] -> update();

    FOR(i, 1, m) {
        scanf("%s", command);
        if (command[0] == 'R') {
            int x, y;
            in(x); in(y);
            scanf("%s", command);
            Node *hr;
            if (x == 1 && y == n) hr = rot;
            if (x == 1 && y != n) {
                find_key(y + 1) -> splay(nul);
                hr = rot -> ch[0];
            }
            if (x != 1 && y == n) {
                find_key(x - 1) -> splay(nul);
                hr = rot -> ch[1];
            }
            if (x != 1 && y != n) {
                find_key(x - 1) -> splay(nul);
                find_key(y + 1) -> splay(rot);
                hr = find_key(y + 1) -> ch[0];
            }
            hr -> pushdown();
            hr -> ist_dn = false; hr -> isset = true;
            hr -> wt_set = (command[0] == '(' ? 1 : -1);
            hr -> pushdown();
            while (hr -> fa != nul) {
                hr = hr -> fa;
                hr -> update();
            }
        }
        else if (command[0] == 'Q') {
            int x, y;
            in(x); in(y);
            Node *hr;
            if (x == 1 && y == n) hr = rot;
            if (x == 1 && y != n) {
                find_key(y + 1) -> splay(nul);
                hr = rot -> ch[0];
            }
            if (x != 1 && y == n) {
                find_key(x - 1) -> splay(nul);
                hr = rot -> ch[1];
            }
            if (x != 1 && y != n) {
                find_key(x - 1) -> splay(nul);
                find_key(y + 1) -> splay(rot);
                hr = find_key(y + 1) -> ch[0];
            }
            hr -> pushdown();
            printf("%d\n", (-(hr -> lmin) + 1) / 2 + (hr -> rmax + 1) / 2);
        }
        else if (command[0] == 'S') {
            int x, y;
            in(x); in(y);
            Node *hr;
            if (x == 1 && y == n) hr = rot;
            if (x == 1 && y != n) {
                find_key(y + 1) -> splay(nul);
                hr = rot -> ch[0];
            }
            if (x != 1 && y == n) {
                find_key(x - 1) -> splay(nul);
                hr = rot -> ch[1];
            }
            if (x != 1 && y != n) {
                find_key(x - 1) -> splay(nul);
                find_key(y + 1) -> splay(rot);
                hr = find_key(y + 1) -> ch[0];
            }
            hr -> isa ^= 1;
            hr -> pushdown();
        }
        else if (command[0] == 'I') {
            int x, y;
            in(x); in(y);
            Node *hr;
            if (x == 1 && y == n) hr = rot;
            if (x == 1 && y != n) {
                find_key(y + 1) -> splay(nul);
                hr = rot -> ch[0];
            }
            if (x != 1 && y == n) {
                find_key(x - 1) -> splay(nul);
                hr = rot -> ch[1];
            }
            if (x != 1 && y != n) {
                find_key(x - 1) -> splay(nul);
                find_key(y + 1) -> splay(rot);
                hr = find_key(y + 1) -> ch[0];
            }
            hr -> ist_dn ^= 1;
            hr -> pushdown();
            while (hr -> fa != nul) {
                hr = hr -> fa;
                hr -> update();
            }
        }
    }

    return 0;
}

Day2 T1 任务调度


这是一道随机出答案的题目,所以我没有做,直接跳过了。


Day2 T2 XOR和路径


思路

这是一个简单的概率DP。

因为是XOR,所以我们可以逐位求出期望。

假设当前在处理第 k 位,设 f[i] 表示从 i n 异或值为 1 的概率。

则对于 i 的一个连出去的边所指向的节点 j ,如果边权为 1,则对 f[i] 的贡献为

1f[j]deg[i]

如果边权为 0 ,则对 f[i] 的贡献为

f[j]deg[i]

最后别忘了 f[n]=0

代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}

template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}

const int MAXN = 110, MAXM = 10010;
const double eps = 1e-8;

int n, m, cnt, du[MAXN];
int head[MAXN], data[MAXM << 1], nxt[MAXM << 1], flow[MAXM << 1];

double matrix[MAXN][MAXN];

void add(int x, int y, int z)
{
    nxt[cnt] = head[x]; data[cnt] = y; flow[cnt] = z; head[x] = cnt++;
    if (x != y) {nxt[cnt] = head[y]; data[cnt] = x; flow[cnt] = z; head[y] = cnt++;}
}

void gauss_george()
{
    FOR(i, 1, n) {
        double maxv = -1; int maxq;
        FOR(j, i, n) {
            if (fabs(matrix[j][i]) > maxv) {
                maxv = fabs(matrix[j][i]);
                maxq = j;
            }
        }

        if (fabs(matrix[maxq][i] - maxv) > eps) {
            assert(0);
        }
        if (fabs(matrix[maxq][i]) < eps) continue;

        FOR(j, 1, n + 1) {
            double tmp = matrix[i][j];
            matrix[i][j] = matrix[maxq][j];
            matrix[maxq][j] = tmp;
        }

        double chu = matrix[i][i];
        FOR(j, 1, n + 1) matrix[i][j] /= chu;

        FOR(j, 1, n) {
            if (j != i) {
                if (fabs(matrix[j][i]) < eps) continue;
                double chu = matrix[j][i];
                FOR(k, 1, n + 1)
                    matrix[j][k] -= matrix[i][k] * chu;
            }
        }
    }
}

int main()
{
    freopen("xor.in", "r", stdin);
    freopen("xor.out", "w", stdout);

    in(n); in(m);

    memset(head, -1, sizeof head);

    FOR(i, 1, m) {
        int u, v, w;
        in(u); in(v); in(w);

        add(u, v, w);
        du[v]++; if (u != v) du[u]++;
    }

    double ans = 0;

    FOR(i, 1, 31) {
        memset(matrix, 0, sizeof matrix);
        FOR(j, 1, n - 1) {
            matrix[j][j] = du[j];
            for (int k = head[j]; k != -1; k = nxt[k]) {
                if (flow[k] & (1 << (i - 1))) {
                    matrix[j][data[k]] += 1.0;
                    matrix[j][n + 1] += 1.0;
                }
                else
                    matrix[j][data[k]] -= 1.0;
            }
        }
        matrix[n][n] = 1;
        gauss_george();
        ans += (1 << (i - 1)) * matrix[1][n + 1] / matrix[1][1];
    }

    printf("%.3lf\n", ans);

    return 0;
}

Day2 T3 数矩形


思路

这又是一道玄学题。

我们找到每条线段的中点,然后按照中点为第一关键字,线段的长度为第二关键字进行排序。

然后对于每个线段,暴力找前面所有跟它中点重合且长度相等的线段……

这样就可以过了。

代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(), f = 1; x = 0;
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') ch = getchar(), f = -1;
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    x *= f;
}

template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}

const int MAXN = 2010;

struct Node {
    LL lenth;
    int posx, posy, rposx[2], rposy[2];
} pos[MAXN * MAXN];

int n, cnt, x[MAXN], y[MAXN];

LL two(LL x) {return x * x;}

bool cmp(const Node &x, const Node &y)
{
    bool t1 = x.posx < y.posx;
    bool t2 = x.posx == y.posx && x.posy < y.posy;
    bool t3 = x.posx == y.posx && x.posy == y.posy && x.lenth < y.lenth;
    return t1 || t2 || t3;
}

LL abs(LL x) {return x > 0 ? x : -x;}

int main()
{
    freopen("rectangle.in", "r", stdin);
    freopen("rectangle.out", "w", stdout);

    in(n);

    FOR(i, 1, n) {
        in(x[i]); in(y[i]);
    }

    FOR(i, 1, n) FOR(j, i + 1, n) {
        pos[++cnt].posx = x[i] + x[j], pos[cnt].posy = y[i] + y[j];
        pos[cnt].lenth = two(x[i] - x[j]) + two(y[i] - y[j]);
        pos[cnt].rposx[0] = x[i]; pos[cnt].rposx[1] = x[j];
        pos[cnt].rposy[0] = y[i]; pos[cnt].rposy[1] = y[j];
    }

    std::sort(pos + 1, pos + cnt + 1, cmp);

    LL ans = -1;

    FOR(i, 1, cnt) {
        for (int j = i - 1;
             j >= 1 && pos[j].lenth == pos[i].lenth
                 && pos[i].posx == pos[j].posx && pos[i].posy == pos[j].posy;
             j--)
            chkmax(ans,
                   abs(2ll * pos[i].rposx[0] * pos[j].rposy[0] - 2ll * pos[i].rposy[0] * pos[j].rposx[0]
                   + 1ll * pos[j].rposx[0] * pos[i].posy - 1ll * pos[j].rposy[0] * pos[i].posx
                       + 1ll * pos[i].posx * pos[i].rposy[0] - 1ll * pos[i].posy * pos[i].rposx[0]));
    }

    printf("%lld\n", ans);

    return 0;
}

Day2 T4 卡农


思路

我们可以先算出可以记片段之间顺序的方案数,因为两两不同,所以最后除以 m! 就可以了。

f[i] 表示前 i 个片段满足题意的方案数。

如果前 i1 个片段已经决定了,那么第 i 个片段也可以由奇偶关系决定了。

那么答案就为 P(2n1,i1) 减去不合法的方案。

这里的 P(2n1,i1) 可以递推求。

不合法的方案只有两种可能:

  1. i1 个片段已经满足偶数要求了,那么第 i 个片段必须是空集合,不符合规定。
    所以要减去 f[i1]

  2. 被决定出来的第 i 个片段重复了。
    那么与它重复的那个片段有 i1 个位置可以选择。
    并且如果除了这两个片段之外,其它的片段均满足偶数条件,那么这两个片段一定相同。
    而这个片段本身也有 2n1(i2) 种可能。
    所以要减去 f[i2]×(i1)×(2n1(i2))

所以就可以直接求了。

代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}

template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}

const int MOD = 100000007;
const int MAXN = 1000010;

int n, m;
LL f[MAXN];

LL power(LL x, LL y)
{
    LL ret = 1;
    while (y) {
        if (y & 1) ret = ret * x % MOD;
        x = x * x % MOD;
        y >>= 1;
    }
    return ret;
}

int main()
{
    freopen("canon.in", "r", stdin);
    freopen("canon.out", "w", stdout);

    in(n); in(m);

    LL pre = 1, po = (power(2, n) - 1 + MOD) % MOD;

    pre = pre * po % MOD;
    po = (po - 1 + MOD) % MOD;

    f[0] = 1; f[1] = 0;

    FOR(i, 2, m) {
        f[i] = pre;
        f[i] = (f[i] - f[i - 1] + MOD) % MOD;
        f[i] = (f[i] - f[i - 2] * (i - 1) % MOD * (power(2, n) - 1 - (i - 2)) % MOD + MOD) % MOD;
        pre = pre * po % MOD;
        po = (po - 1 + MOD) % MOD;
    }

    LL ret = 1; FOR(i, 1, m) ret = ret * i % MOD;
    printf("%lld\n", f[m] * power(ret, MOD - 2) % MOD);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值