1. 程式人生 > >【NOIP2018模擬10.26】總結

【NOIP2018模擬10.26】總結

T1

這個題考試的時候仔細想想應該可以切掉的。。。

先轉換成計數問題,最後求一下逆元,除以區間個數就ok。 對於20%20\%的資料,可以按照題意模擬。 對於40%40\%的資料,觀察到線段樹上每個節點的貢獻獨立,列舉所有節點,一個節點的貢獻是可以O(1)O(1)計算的。 對於60%60\%的資料,觀察到線段樹上每一層的貢獻獨立,可以考慮如何求第ii層對答案的貢獻。 我們先考慮一個節點作為包含左端點的情況,因為它作為包含右端點的情況是和包含左端點一樣的,我們只需要乘以22就可以了。

(被算x次意思是在x個區間查詢裡它有貢獻) 在草稿紙上搗鼓幾下可以發現,第ii層所有的作為左兒子的節點總共要被算2

n12^{n-1}次。 所有的作為右兒子的節點被計算的次數如下: 11 1+51+5 1+3+5+71+3+5+7 ...... 可以發現,第ii層所有作為右兒子的節點被計算的次數構成了一個首項為1,末項為2n2ni+1+12^n-2^{n-i+1}+1,項數為2i12^{i-1} 的等差數列。 套用等差數列求和公式,可得到所有右兒子計算次數為: (2n2ni+1+2)(2i1)2\frac{(2^n-2^{n-i+1}+2)(2^{i-1})}{2} 所以答案可以表示如下: 2
i=1ni(2n1+(2n2ni+1+2)(2i1)2)2\sum_{i=1}^{n}i(2^{n-1}+\frac{(2^n-2^{n-i+1}+2)(2^{i-1})}{2})
化簡之後得到: (2n1+1)i=1ni2i(2^{n-1}+1)\sum_{i=1}^{n}i2^i 直接計算這個式子,複雜度O(n)O(n)

對於100%100\%的資料,關鍵是這個怎麼算: i=1ni2i\sum_{i=1}^{n}i2^i =i=1n2i+i=2

n2i+i=3n2i+...=\sum_{i=1}^{n}2^i+\sum_{i=2}^{n}2^i+\sum_{i=3}^{n}2^i+... =2n+1120+2n+11(20+21)+2n+11(20+21+22)+...=2^{n+1}-1-2^0+2^{n+1}-1-(2^0+2^1)+2^{n+1}-1-(2^0+2^1+2^2)+... =n2n+1n(211)(221)(231)...=n2^{n+1}-n-(2^1-1)-(2^2-1)-(2^3-1)-... =n2n+1n(2n+1120n)=n2^{n+1}-n-(2^{n+1}-1-2^0-n) =(n1)2n+1+2=(n-1)2^{n+1}+2 於是答案就是: (2n1+1)((n1)2n+1+2)(2^{n-1}+1)((n-1)2^{n+1}+2) 用快速冪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\%的隨機資料,將詢問離線,把每個點向上更新答案。

對於100%100\%的資料。考慮到各個質因子對答案的影響是獨立的,因此可以分開統計,我們將每個點的aia_i和詢問的cc都分解質因數,將質因數相同放在一起,容易得出其指數是所有指數與詢問取minmin的和。我們可以按照指數排序,這樣對於每個質因子,就是一個三維偏序問題:第一維是dfsdfs序,修改的點要在詢問子樹內,第二維是深度,距離詢問的點必須&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]