您的位置:首页 > 其它

HNOI2011解题报告

2017-05-19 21:12 218 查看

HNOI2011解题报告

Author: Pengyihao

Day1 T1 数学作业

题意

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

1≤n≤1018,1≤m≤109

思路

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

nowans=lastans∗10k+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] 的贡献为

1−f[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

= 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 个片段满足题意的方案数。

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

那么答案就为 P(2n−1,i−1) 减去不合法的方案。

这里的 P(2n−1,i−1) 可以递推求。

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

前 i−1 个片段已经满足偶数要求了,那么第 i 个片段必须是空集合,不符合规定。

所以要减去 f[i−1]。

被决定出来的第 i 个片段重复了。

那么与它重复的那个片段有 i−1 个位置可以选择。

并且如果除了这两个片段之外,其它的片段均满足偶数条件,那么这两个片段一定相同。

而这个片段本身也有 2n−1−(i−2) 种可能。

所以要减去 f[i−2]×(i−1)×(2n−1−(i−2))

所以就可以直接求了。

代码

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: