题面又臭又长,我放个链接算了。
https://share.weiyun.com/5xk2rzi
T1
这个题考试的时候仔细想想应该可以切掉的。。。
先转换成计数问题,最后求一下逆元,除以区间个数就ok。
对于
20
%
20\%
20%的数据,可以按照题意模拟。
对于
40
%
40\%
40%的数据,观察到线段树上每个节点的贡献独立,枚举所有节点,一个节点的贡献是可以
O
(
1
)
O(1)
O(1)计算的。
对于
60
%
60\%
60%的数据,观察到线段树上每一层的贡献独立,可以考虑如何求第
i
i
i层对答案的贡献。
我们先考虑一个节点作为包含左端点的情况,因为它作为包含右端点的情况是和包含左端点一样的,我们只需要乘以
2
2
2就可以了。
(被算x次意思是在x个区间查询里它有贡献)
在草稿纸上捣鼓几下可以发现,第
i
i
i层所有的作为左儿子的节点总共要被算
2
n
−
1
2^{n-1}
2n−1次。
所有的作为右儿子的节点被计算的次数如下:
1
1
1
1
+
5
1+5
1+5
1
+
3
+
5
+
7
1+3+5+7
1+3+5+7
.
.
.
...
...
可以发现,第
i
i
i层所有作为右儿子的节点被计算的次数构成了一个首项为1,末项为
2
n
−
2
n
−
i
+
1
+
1
2^n-2^{n-i+1}+1
2n−2n−i+1+1,项数为
2
i
−
1
2^{i-1}
2i−1 的等差数列。
套用等差数列求和公式,可得到所有右儿子计算次数为:
(
2
n
−
2
n
−
i
+
1
+
2
)
(
2
i
−
1
)
2
\frac{(2^n-2^{n-i+1}+2)(2^{i-1})}{2}
2(2n−2n−i+1+2)(2i−1)
所以答案可以表示如下:
2
∑
i
=
1
n
i
(
2
n
−
1
+
(
2
n
−
2
n
−
i
+
1
+
2
)
(
2
i
−
1
)
2
)
2\sum_{i=1}^{n}i(2^{n-1}+\frac{(2^n-2^{n-i+1}+2)(2^{i-1})}{2})
2∑i=1ni(2n−1+2(2n−2n−i+1+2)(2i−1))
化简之后得到:
(
2
n
−
1
+
1
)
∑
i
=
1
n
i
2
i
(2^{n-1}+1)\sum_{i=1}^{n}i2^i
(2n−1+1)∑i=1ni2i
直接计算这个式子,复杂度
O
(
n
)
O(n)
O(n)
对于
100
%
100\%
100%的数据,关键是这个怎么算:
∑
i
=
1
n
i
2
i
\sum_{i=1}^{n}i2^i
∑i=1ni2i
=
∑
i
=
1
n
2
i
+
∑
i
=
2
n
2
i
+
∑
i
=
3
n
2
i
+
.
.
.
=\sum_{i=1}^{n}2^i+\sum_{i=2}^{n}2^i+\sum_{i=3}^{n}2^i+...
=∑i=1n2i+∑i=2n2i+∑i=3n2i+...
=
2
n
+
1
−
1
−
2
0
+
2
n
+
1
−
1
−
(
2
0
+
2
1
)
+
2
n
+
1
−
1
−
(
2
0
+
2
1
+
2
2
)
+
.
.
.
=2^{n+1}-1-2^0+2^{n+1}-1-(2^0+2^1)+2^{n+1}-1-(2^0+2^1+2^2)+...
=2n+1−1−20+2n+1−1−(20+21)+2n+1−1−(20+21+22)+...
=
n
2
n
+
1
−
n
−
(
2
1
−
1
)
−
(
2
2
−
1
)
−
(
2
3
−
1
)
−
.
.
.
=n2^{n+1}-n-(2^1-1)-(2^2-1)-(2^3-1)-...
=n2n+1−n−(21−1)−(22−1)−(23−1)−...
=
n
2
n
+
1
−
n
−
(
2
n
+
1
−
1
−
2
0
−
n
)
=n2^{n+1}-n-(2^{n+1}-1-2^0-n)
=n2n+1−n−(2n+1−1−20−n)
=
(
n
−
1
)
2
n
+
1
+
2
=(n-1)2^{n+1}+2
=(n−1)2n+1+2
于是答案就是:
(
2
n
−
1
+
1
)
(
(
n
−
1
)
2
n
+
1
+
2
)
(2^{n-1}+1)((n-1)2^{n+1}+2)
(2n−1+1)((n−1)2n+1+2)
用快速幂
O
(
l
o
g
n
)
O(logn)
O(logn)的时间计算答案,模数在int
范围内,不会爆long long
。
Code:
#include <cstdio>
#include <cstring>
#include <cstdlib>
typedef long long ll;
const ll P = 1e9 + 7;
ll pow(ll a, ll b)
{
a %= P;
ll ret = 1;
while (b)
{
if (b & 1) ret = ret * a % P;
a = a * a % P, b >>= 1;
}
return ret;
}
ll n;
int main()
{
//freopen("A.in", "r", stdin);
//freopen("A.out", "w", stdout);
scanf("%lld", &n);
ll p1 = pow(2, n - 1), p = p1 * 2 % P, p2 = p * 2 % P;
ll sum = (p1 + 1) * ((n - 1) % P * p2 % P + 2) % P;
printf("%lld\n", 2 * sum % P * pow(p, P - 2) % P * pow(p + 1, P - 2) % P);
fclose(stdin);
fclose(stdout);
return 0;
}
T2
大坑待填。
T3
对于
20
%
20\%
20%的数据,按照题意暴力。
对于
20
%
20\%
20%的随机数据,将询问离线,把每个点向上更新答案。
对于 100 % 100\% 100%的数据。考虑到各个质因子对答案的影响是独立的,因此可以分开统计,我们将每个点的 a i a_i ai和询问的 c c c都分解质因数,将质因数相同放在一起,容易得出其指数是所有指数与询问取 m i n min min的和。我们可以按照指数排序,这样对于每个质因子,就是一个三维偏序问题:第一维是 d f s dfs dfs序,修改的点要在询问子树内,第二维是深度,距离询问的点必须 < k <k <k,第三位是指数,对于小于询问的指数,我们需要统计它们的和,对于大于询问的指数,我们只需要知道有多少个就行了。于是cdq分治一下,注意常数,就没问题了。
Code:
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7, MAX = 1e7 + 7, P = 998244353;
inline int read()
{
int x = 0, f = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
return f ? -x : x;
}
inline int qpow(int a, int b)
{
int ret = 1;
while (b)
{
if (b & 1) ret = ret * 1ll * a % P;
a = a * 1ll * a % P, b >>= 1;
}
return ret;
}
int n, q, k, x[N], c[N];
struct note { int id, ct; };
vector<note> incl[MAX / 10];
int tot, tt, st[N], to[N << 1], nx[N << 1], dfn[N], out[N], dep[N], a[N];
void add(int u, int v) { to[++tot] = v, nx[tot] = st[u], st[u] = tot; }
int ret[N][2], ans[N];
struct ques { int typ, x, y, z, val; };
vector<ques> all[MAX / 10];
ques arr[N * 10];
int pr, check[MAX], prs[MAX / 10], mp[MAX];
void dfs(int u)
{
dfn[u] = ++tt;
for (int i = st[u]; i; i = nx[i]) if (!dfn[to[i]]) dep[to[i]] = dep[u] + 1, dfs(to[i]);
out[u] = tt;
}
void init()
{
for (int i = 2; i <= MAX - 7; i++)
{
if (!check[i]) prs[++pr] = i, mp[i] = pr;
for (int j = 1; j <= pr; j++)
{
if (i * prs[j] > MAX - 7) break;
check[i * prs[j]] = 1, mp[i * prs[j]] = j;
if (i % prs[j] == 0) break;
}
}
n = read(), q = read(), k = read() - 1;
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1, u, v; i < n; i++) u = read(), v = read(), add(u, v), add(v, u);
dfs(1);
for (int i = 1; i <= q; i++) x[i] = read(), c[i] = read();
for (int i = 1; i <= q; i++)
{
ans[i] = 1;
while (c[i] > 1)
{
int cnt = 0, t = mp[c[i]];
while (c[i] % prs[t] == 0) c[i] /= prs[t], cnt++;
all[t].push_back((ques){i, out[x[i]], dep[x[i]] + k, cnt, 1});
all[t].push_back((ques){i, dfn[x[i]] - 1, dep[x[i]] + k, cnt, -1});
incl[t].push_back((note){i, cnt});
}
}
for (int i = 1; i <= n; i++)
while (a[i] > 1)
{
int cnt = 0, t = mp[a[i]];
while (a[i] % prs[t] == 0) a[i] /= prs[t], cnt++;
if (incl[t].empty()) continue;
all[t].push_back((ques){0, dfn[i], dep[i], cnt, 0});
}
}
int cmp(ques a, ques b) { return a.x == b.x ? a.typ < b.typ : a.x < b.x; }
int operator<(ques a, ques b) { return a.y <= b.y; }
int tr[2][40];
void add(int k, int po, int v) { for (; po <= 24; po += (po & (-po))) tr[k][po] += v; }
int getsum(int k, int po)
{
int s = 0;
for (; po; po -= (po & (-po))) s += tr[k][po];
return s;
}
void clear(int po) { for (; po <= 24; po += (po & (-po))) tr[0][po] = tr[1][po] = 0; }
ques tmp[N * 5];
void cdq(int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
cdq(l, mid), cdq(mid + 1, r);
int len = 0, p1 = l, p2 = mid + 1;
while (p1 <= mid && p2 <= r)
if (arr[p1] < arr[p2])
{
if (!arr[p1].typ) add(0, arr[p1].z, 1), add(1, arr[p1].z, arr[p1].z);
tmp[++len] = arr[p1++];
}
else
{
if (arr[p2].typ)
{
ret[arr[p2].typ][0] += arr[p2].val * (getsum(0, 24) - getsum(0, arr[p2].z));
ret[arr[p2].typ][1] += arr[p2].val * getsum(1, arr[p2].z);
}
tmp[++len] = arr[p2++];
}
while (p1 <= mid)
{
if (!arr[p1].typ) add(0, arr[p1].z, 1), add(1, arr[p1].z, arr[p1].z);
tmp[++len] = arr[p1++];
}
while (p2 <= r)
{
if (arr[p2].typ)
{
ret[arr[p2].typ][0] += arr[p2].val * (getsum(0, 24) - getsum(0, arr[p2].z));
ret[arr[p2].typ][1] += arr[p2].val * getsum(1, arr[p2].z);
}
tmp[++len] = arr[p2++];
}
memset(tr, 0, sizeof(tr));
for (int i = 1; i <= len; i++) arr[l + i - 1] = tmp[i];
}
void solve()
{
for (int i = 1; i <= pr; i++)
{
int siz = all[i].size();
if (incl[i].empty()) continue;
for (int j = 0; j < siz; j++) arr[j + 1] = all[i][j];
sort(arr + 1, arr + siz + 1, cmp);
int siz_ = incl[i].size();
for (int j = 0; j < siz_; j++)
{
int ind = incl[i][j].id;
ret[ind][0] = ret[ind][1] = 0;
}
cdq(1, siz);
for (int j = 0; j < siz_; j++)
{
int ind = incl[i][j].id;
ans[ind] = ans[ind] * 1ll * qpow(prs[i], incl[i][j].ct * ret[ind][0] + ret[ind][1]) % P;
}
}
for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
}
int main()
{
freopen("C.in", "r", stdin);
freopen("C.out", "w", stdout);
init();
solve();
fclose(stdin);
fclose(stdout);
return 0;
}
辣鸡出题人什么鬼题面。