【NOIP2018模拟10.26】总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/er111er/article/details/83443092

题面又臭又长,我放个链接算了。
https://share.weiyun.com/5xk2rzi

T1

这个题考试的时候仔细想想应该可以切掉的。。。

先转换成计数问题,最后求一下逆元,除以区间个数就ok。
对于 20 % 20\% 的数据,可以按照题意模拟。
对于 40 % 40\% 的数据,观察到线段树上每个节点的贡献独立,枚举所有节点,一个节点的贡献是可以 O ( 1 ) O(1) 计算的。
对于 60 % 60\% 的数据,观察到线段树上每一层的贡献独立,可以考虑如何求第 i i 层对答案的贡献。
我们先考虑一个节点作为包含左端点的情况,因为它作为包含右端点的情况是和包含左端点一样的,我们只需要乘以 2 2 就可以了。

(被算x次意思是在x个区间查询里它有贡献)
在草稿纸上捣鼓几下可以发现,第 i i 层所有的作为左儿子的节点总共要被算 2 n 1 2^{n-1} 次。
所有的作为右儿子的节点被计算的次数如下:
1 1
1 + 5 1+5
1 + 3 + 5 + 7 1+3+5+7
. . . ...
可以发现,第 i i 层所有作为右儿子的节点被计算的次数构成了一个首项为1,末项为 2 n 2 n i + 1 + 1 2^n-2^{n-i+1}+1 ,项数为 2 i 1 2^{i-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 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 n 1 + 1 ) i = 1 n i 2 i (2^{n-1}+1)\sum_{i=1}^{n}i2^i
直接计算这个式子,复杂度 O ( n ) O(n)

对于 100 % 100\% 的数据,关键是这个怎么算:
i = 1 n i 2 i \sum_{i=1}^{n}i2^i
= 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+...
= 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)+...
= 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)-...
= n 2 n + 1 n ( 2 n + 1 1 2 0 n ) =n2^{n+1}-n-(2^{n+1}-1-2^0-n)
= ( n 1 ) 2 n + 1 + 2 =(n-1)2^{n+1}+2
于是答案就是:
( 2 n 1 + 1 ) ( ( n 1 ) 2 n + 1 + 2 ) (2^{n-1}+1)((n-1)2^{n+1}+2)
用快速幂 O ( l o g n ) 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\% 的随机数据,将询问离线,把每个点向上更新答案。

对于 100 % 100\% 的数据。考虑到各个质因子对答案的影响是独立的,因此可以分开统计,我们将每个点的 a i a_i 和询问的 c c 都分解质因数,将质因数相同放在一起,容易得出其指数是所有指数与询问取 m i n min 的和。我们可以按照指数排序,这样对于每个质因子,就是一个三维偏序问题:第一维是 d f s dfs 序,修改的点要在询问子树内,第二维是深度,距离询问的点必须 &lt; k &lt;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;
}

辣鸡出题人什么鬼题面。

扫描二维码关注公众号,回复: 3863800 查看本文章

猜你喜欢

转载自blog.csdn.net/er111er/article/details/83443092
今日推荐