Goodbye Jihai 部分題目簡要題解

從這裏開始

  好像那天正好在路上,成功錯過了打(掉)比賽(rating)的機會。

  (據可靠消息稱,神仙 jerome_wei 不走水就捧杯了。

  因爲我不太會二次剩餘,所以現在還沒補 E。

Problem A 新年的促銷

  dp 即可

  不難注意到假設最終一共帶走了 $k$ 袋大米,那麼購買的一定是其中價格最小的若干袋。

  考慮枚舉買走的大米的最大價值,記錄一下買了多少袋,以及花了多少錢,然後就計算答案了。

  時間複雜度 $O(n^2m)$

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

const int N = 305, M = 1e3 + 5;
const ll llf = (signed ll) (~0ull >> 3);

typedef class Item {
	public:
		int w, p;
} Item;


int n, m, a, b;
Item it[N];
ll ans[M];
ll f[N][M];

int main() {
	scanf("%d%d%d%d", &n, &m, &a, &b);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &it[i].w);
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &it[i].p);
	}
	sort(it + 1, it + n + 1, [&] (const Item& a, const Item& b) {	return a.w > b.w;	});
	for (int i = 0; i <= m; i++)
		ans[i] = -llf;
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= m; j++)
			f[i][j] = -llf;
	f[0][0] = 0;	
	for (int i = 1; i <= n; i++) {
		int id = i;
		for (int j = i + 1; j <= n; j++)
			if (it[j].p < it[id].p)
				id = j;
		rotate(it + i, it + id, it + id + 1);	
		for (int j = i; j; j--) {
			for (int k = m; k >= it[i].p; k--) {
				f[j][k] = max(f[j][k], f[j - 1][k - it[i].p] + it[i].w);
			}
		}
		ll sum = 0;
		int cnt = 0;
		for (int j = 1; j <= i; j++) {
			int x = (j / a) + (j / b);
			while (i + cnt < n && cnt < x)
				sum += it[i + ++cnt].w;
			for (int k = 1; k <= m; k++) {
				ans[k] = max(ans[k], sum + f[j][k]);
			}	
		}
	}
	ans[0] = 0;
	for (int i = 1; i <= m; i++) {
		ans[i] = max(ans[i], ans[i - 1]);
		printf("%lld ", ans[i]);
	}
	return 0;
}

Problem B 新年的新航線

  可以猜想 $n > 3$ 一定有解。

  考慮度爲 $2$ 的點 $p$,與它相鄰的點是 $u, v$,那麼它在生成樹上的度數只可能爲 1。那麼相當於會對 $u, v$ 中其中一個點的度數加上 1。因爲是三角剖分,所以一定存在邊 $(u, v)$,我們給 $(u, v)$ 標上數 $i$。

  所以現在的問題是給一個邊界上有標數的多邊形找一個生成樹,滿足每個點的度數要麼爲 1,要麼至少爲 3。簡單討論一下可以發現 $n = 4$ 始終有解。考慮不斷減少一個 $n > 4$ 的多邊形的點數。

  仍然考慮 2 度點 $p$,考慮仍然硬點它和其他在多邊形上的點恰好有 1 條邊。不妨設和它相鄰的點是 $u, v$。

  • 如果 $(p, u), (p, v)$ 無標數,那麼給 $(u, v)$ 標上數 $p$,然後刪掉 $p$。
  • 如果 $(p, u)$ 有標數 $s$,$(p, v)$ 無標數,那麼連上邊 $(s, u), (p, u)$,然後刪掉 $p$
  • 如果 $(p, u), (p, v)$ 有標數,那麼它們標數均向 $p$ 連邊,然後給 $(u, v)$ 標上 $p$,再刪掉 $p$

  不難做到線性。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

const int N = 1e6 + 5, N2 = N << 1;

#define pii pair<int, int>

int n;
int d[N];
boolean ban[N2];
int us[N2], vs[N2];
vector<int> G[N];
vector<pii> lab[N];
vector<pii> ans;

int main() {
	scanf("%d", &n);
	if (n == 3) {
		puts("-1");
		return 0;
	}
	int ce = 0;
	for (int i = 1; i <= n; i++) {
		int u = i;
		int v = (i == n) ? (1) : (i + 1);
		++ce;
		d[u]++, d[v]++;
		us[ce] = u, vs[ce] = v;
		G[u].push_back(i);
		G[v].push_back(i);
	}
	for (int i = 1, u, v; i <= n - 3; i++) {
		scanf("%d%d", &u, &v);
		++ce;
		us[ce] = u, vs[ce] = v;
		G[u].push_back(ce);
		G[v].push_back(ce);
		d[u]++, d[v]++;
	}
	queue<int> Q;
	for (int i = 1; i <= n; i++) {
		if (d[i] == 2) {
			Q.push(i);
		}
	}
	for (int _ = 1; _ <= n - 4; _++) {
		int p = Q.front();
		Q.pop();
		vector<int> tmp;
		for (auto eid : G[p]) {
			if (!ban[eid]) {
				tmp.push_back(eid);
			}
		}
		G[p].clear();
		assert(tmp.size() == 2u);
		int ex = tmp[0], ey = tmp[1];
		int u = us[ex] ^ vs[ex] ^ p;
		int v = us[ey] ^ vs[ey] ^ p;
		int lx = 0, ly = 0;
		for (auto par : lab[p]) {
			int e = par.first;
			int w = par.second;
			if (e == u) {
				lx = w;
			} else if (e == v) {
				ly = w;
			}
		}
		ban[ex] = ban[ey] = true;
		lab[p].clear();
		if (lx && ly) {
			ans.emplace_back(lx, p);
			ans.emplace_back(ly, p);
			lab[u].emplace_back(v, p);
			lab[v].emplace_back(u, p);
		} else if (!lx && !ly) {
			lab[u].emplace_back(v, p);
			lab[v].emplace_back(u, p);
		} else if (lx && !ly) {
			ans.emplace_back(lx, u);
			ans.emplace_back(p, u);
		} else {
			ans.emplace_back(ly, v);
			ans.emplace_back(p, v);
		}
		if (--d[u] == 2)
			Q.push(u);
		if (--d[v] == 2)
			Q.push(v);
	}
	vector<int> rest;
	for (int i = 1; i <= n; i++) {
		if (!G[i].empty()) {
			rest.push_back(i);
		}
	}
	sort(rest.begin(), rest.end(), [&] (int x, int y) {	return d[x] > d[y];	});
	
	assert(rest.size() == 4u);

	auto have_nearby = [&] (int x) {
		for (auto y : lab[x]) {
			int e = y.first;
			if (!G[e].empty()) {
				return true;
			}
		}
		return false;
	};
	
	auto link_nearby = [&] (int p) {
		for (auto y : lab[p]) {
			int e = y.first;
			if (G[e].empty())
				continue;
			int q = y.second;
			ans.emplace_back(p, q);
		}
	};

	boolean flag0 = have_nearby(rest[0]);
	boolean flag1 = have_nearby(rest[1]);
	if (flag0 && flag1) {
		link_nearby(rest[0]);
		link_nearby(rest[1]);
		ans.emplace_back(rest[0], rest[1]);
		ans.emplace_back(rest[2], rest[0]);
		ans.emplace_back(rest[3], rest[1]);
	} else if (flag0) {
		link_nearby(rest[0]);
		ans.emplace_back(rest[0], rest[1]);
		ans.emplace_back(rest[0], rest[2]);
		ans.emplace_back(rest[0], rest[3]);
	} else {
		link_nearby(rest[1]);
		ans.emplace_back(rest[1], rest[0]);
		ans.emplace_back(rest[1], rest[2]);
		ans.emplace_back(rest[1], rest[3]);
	}
	assert((signed) ans.size() == n - 1);
	for (auto par : ans) {
		printf("%d %d\n", par.first, par.second);
	}
	return 0;
}

Problem C 新年的復讀機

  相信三方的區間 dp 大家都會。

  如果 $n = 1$ 的時候答案爲 0。

  如果 $n > 1$ ,每個數一定會對答案有貢獻,考慮原來的數合併的時候不會新產生貢獻。考慮合併 gcd 爲 $g_1$ 和 $g_2$ 的兩段,它們的長度都至少爲 2,兩段的長度分別爲 $l_1, l_2$,不妨設 $g_1 \leqslant g_2$,那麼至少會額外花費 $g_1 + g_2 (l_2 - 1)$ 的代價,而考慮不斷將第二段中的數依次加入第一段中,至多會花費 $g_1 l_2$ 的代價。

  因此存在一種最優策略是從一個點開始,每次不斷將左側或者右側的點加進來。

  不難發現向一邊拓展的時候一定會拓展到 gcd 發生變化爲止,否則做一些簡單調整可以讓答案更優。

  然後粗略分析得到了狀態數爲 $O(n\log^2 V)$  的 dp 做法。

  冷靜一下仔細分析發現,每個狀態 $[l, r]$ 一定滿足要麼 $l = 1$ 或者 $r = n$,要麼 $r$ 是從 $l$ 開始的前綴 gcd 發生變化的位置,要麼 $l$ 是從 $r$ 開始的後綴 gcd 發生變化的位置。因此狀態數爲 $O(n\log V)$。

  經過一些簡單預處理能夠讓轉移做到 $O(1)$。

  常數太大,卡不過去,sad.....

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

const int N = 2e5 + 5, M = 21000000;

#define ll long long 
#define pil pair<int, ll>
#define pii pair<int, int>

const ll llf = (signed ll) (~0ull >> 2);

template <typename T>
T gcd(T a, T b) {
	return (!b) ? a : gcd(b, a % b);
}
template <typename T>
void vmin(T& a, T b) {
	(a > b) && (a = b); 
}

typedef class Status {
	public:
		int l, r;
		ll v, f;
		Status *chl, *chr;
		
		Status() {	}
		Status(int l, int r, ll v) : l(l), r(r), v(v), f(llf) {	}

		void update() {
			(chl) && (vmin(chl->f, f + (l - chl->l) * v), 0);
			(chr) && (vmin(chr->f, f + (chr->r - r) * v), 0);
		}
} Status;

int n;
ll a[N];
Status pl[M], *top = pl;
vector<Status*> pre[N], suf[N];

Status* get(int l, int r, ll v) {
	return *top = Status(l, r, v), top++;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", a + i);
	}
	for (int i = 1; i <= n; i++) {
		pre[i].push_back(get(i, i, a[i]));
		ll v = a[i];
		for (auto it = pre[i - 1].rbegin(); it != pre[i - 1].rend(); it++) {
			int pos = (*it)->l;
			v = gcd((*it)->v, v);
			if (v ^ pre[i].back()->v)
				pre[i].push_back(get(pos, i, v));
		}
		if (pre[i].back()->l != 1)
			pre[i].push_back(get(1, i, v));
		reverse(pre[i].begin(), pre[i].end());
		Status *tmp = NULL;
		for (auto x : pre[i])
			x->chl = tmp, tmp = x;
	}
	for (int i = n; i; i--) {
		suf[i].emplace_back(pre[i].back());
		ll v = a[i];
		for (auto it : suf[i + 1]) {
			int pos = it->r;
			v = gcd(v, it->v);
			if (v != suf[i].back()->v)
				suf[i].push_back(get(i, pos, v));
		}
		if (suf[i].back()->r != n)
			suf[i].push_back(get(i, n, v));
		Status* tmp = NULL;
		for (auto it = suf[i].rbegin(); it != suf[i].rend(); it++)
			(*it)->chr = tmp, tmp = *it;
	}
	assert(top - pl < M);
	vector<int> h (n + 1, -1);
	vector<Status*> G;
	vector<int> nxt;	
	for (int i = n; i; i--) {
		for (auto x : pre[i]) {
			G.push_back(x);
			nxt.push_back(h[x->l]);
			h[x->l] = (signed) G.size() - 1;	
		}
	}
	for (int i = 1; i <= n; i++) {
		auto it = suf[i].begin();
		auto _it = suf[i].end();
		for (int _ = h[i]; ~_; _ = nxt[_]) {
			auto& t = G[_];
			while (it != _it && (*it)->r <= t->r)
				it++;
			t->chr = (it == _it) ? (NULL) : (*it);
		}
		h[i] = -1;
	}
	G.clear(), nxt.clear();
	for (int i = n; i; i--) {
		for (auto x : suf[i]) {
			G.push_back(x);
			nxt.push_back(h[x->r]);
			h[x->r] = (signed) G.size() - 1;	
		}
	}
	for (int i = 1; i <= n; i++) {
		auto st = pre[i].begin(), it = st;
		auto _it = pre[i].end();
		for (int _ = h[i]; ~_; _ = nxt[_]) {
			auto& t = G[_];	
			while (it != _it && (*it)->l < t->l)
				it++;
			t->chl = (it == st) ? (NULL) : (*(it - 1));
		}
		h[i] = -1;
	}
	G.clear(), nxt.clear();
	for (Status* p = pl; p != top; p++) {
		int len = p->r - p->l;
		G.push_back(p);
		nxt.push_back(h[len]);
		h[len] = (signed) G.size() - 1;
	}
	for (int _ = h[0]; ~_; _ = nxt[_]) {
		auto& x = G[_];
		x->f = -x->v;
	}
	for (int i = 0; i < n - 1; i++) {
		for (int _ = h[i]; ~_; _ = nxt[_]) {
			G[_]->update();
		}
	}
	ll ans = llf;
	for (int _ = h[n - 1]; ~_; _ = nxt[_]) {
		ans = min(ans, G[_]->f);
	}
	for (int i = 1; i <= n; i++)
		ans += a[i];
	printf("%lld\n", ans);
	return 0;
}

Problem D 新年的追逐戰

  不難注意到 $H$ 的連通塊在 $G_i$ 是上是連通的。

  考慮每個 $G_i$ 選一個連通塊出來,那麼在 $H$ 中會形成多少個連通塊

  • 如果存在一個連通塊大小爲 1,那麼在 $H$ 中這些都是孤立點
  • 如果其中有 $k$ 個是二分圖,那麼會有 $2^{\max\{k - 1, 0\}}$ 個連通塊。證明的話,考慮對其中的二分圖進行黑白染色,如果確定了其中一個圖是在黑點還是在白點,剩下的二分圖中是在黑點還是白點是確定的,充分性的話可以簡單歸納一下。

  相信數 $n$ 個點的帶標號連通二分圖大家都會。

  剩下是個簡單 dp。

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

template <typename T>
void pfill(T* pst, const T* ped, T val) {
	for ( ; pst != ped; *(pst++) = val);
}

template <typename T>
void pcopy(T* pst, const T* ped, T* pval) {
	for ( ; pst != ped; *(pst++) = *(pval++));
}

const int N = 262144;
const int Mod = 998244353;
const int bzmax = 19;
const int g = 3;

void exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
	} else {
		exgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
}

int inv(int a, int Mod) {
	int x, y;
	exgcd(a, Mod, x, y);
	return (x < 0) ? (x + Mod) : (x);
}

template <const int Mod = :: Mod>
class Z {
	public:
		int v;

		Z() : v(0) {	}
		Z(int x) : v(x){	}
		Z(ll x) : v(x % Mod) {	}

		friend Z operator + (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
		}
		friend Z operator - (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
		}
		friend Z operator * (const Z& a, const Z& b) {
			return Z(a.v * 1ll * b.v);
		}
		friend Z operator ~ (const Z& a) {
			return inv(a.v, Mod);
		}
		friend Z operator - (const Z& a) {
			return Z(0) - a;
		}
		Z& operator += (Z b) {
			return *this = *this + b;
		}
		Z& operator -= (Z b) {
			return *this = *this - b;
		}
		Z& operator *= (Z b) {
			return *this = *this * b;
		}
		friend boolean operator == (const Z& a, const Z& b) {
			return a.v == b.v;
		} 
};

typedef Z<> Zi;

Zi qpow(Zi a, int p) {
	if (p < Mod - 1)
		p += Mod - 1;
	Zi rt = 1, pa = a;
	for ( ; p; p >>= 1, pa = pa * pa) {
		if (p & 1) {
			rt = rt * pa;
		}
	}
	return rt;
}

const Zi inv2 ((Mod + 1) >> 1);

class NTT {
	private:
		Zi gn[bzmax + 4], _gn[bzmax + 4];
	public:
		
		NTT() {
			for (int i = 0; i <= bzmax; i++) {
				gn[i] = qpow(Zi(g), (Mod - 1) >> i);
				_gn[i] = qpow(Zi(g), -((Mod - 1) >> i));
			}
		}

		void operator () (Zi* f, int len, int sgn) {
			for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) {
				if (i < j)
					swap(f[i], f[j]);
				for (k = len >> 1; k <= j; j -= k, k >>= 1);
			}
			
			Zi *wn = (sgn > 0) ? (gn + 1) : (_gn + 1), w, a, b;
			for (int l = 2, hl; l <= len; l <<= 1, wn++) {
				hl = l >> 1, w = 1;
				for (int i = 0; i < len; i += l, w = 1) {
					for (int j = 0; j < hl; j++, w *= *wn) {
						a = f[i + j], b = f[i + j + hl] * w;
						f[i + j] = a + b;
						f[i + j + hl] = a - b;
					}
				}
			}

			if (sgn < 0) {
				Zi invlen = ~Zi(len);
				for (int i = 0; i < len; i++) {
					f[i] *= invlen;
				}
			}
		}

		int correct_len(int len) {
			int m = 1;
			for ( ; m <= len; m <<= 1);
			return m;
		}
} NTT;

void pol_inverse(Zi* f, Zi* g, int n) {
	static Zi A[N];
	if (n == 1) {
		g[0] = ~f[0];
	} else {
		int hn = (n + 1) >> 1, t = NTT.correct_len(n << 1 | 1);
		pol_inverse(f, g, hn);
		
		pcopy(A, A + n, f);
		pfill(A + n, A + t, Zi(0));
		pfill(g + hn, g + t, Zi(0));
		NTT(A, t, 1);
		NTT(g, t, 1);
		for (int i = 0; i < t; i++) {
			g[i] = g[i] * (Zi(2) - g[i] * A[i]);
		}
		NTT(g, t, -1);
		pfill(g + n, g + t, Zi(0));
	}
}

void pol_sqrt(Zi* f, Zi* g, int n) {
	static Zi A[N], B[N];
	if (n == 1) {
		g[0] = f[0];
	} else {
		int hn = (n + 1) >> 1, t = NTT.correct_len(n + n);
		
		pol_sqrt(f, g, hn);

		pfill(g + hn, g + n, Zi(0));
		for (int i = 0; i < hn; i++)
			A[i] = g[i] + g[i];
		pfill(A + hn, A + t, Zi(0));
		pol_inverse(A, B, n);
		pcopy(A, A + n, f);
		pfill(A + n, A + t, Zi(0));
		NTT(A, t, 1);
		NTT(B, t, 1);
		for (int i = 0; i < t; i++)
			A[i] *= B[i];
		NTT(A, t, -1);
		for (int i = 0; i < n; i++)
			g[i] = g[i] * inv2 + A[i];
	}
}

typedef class Poly : public vector<Zi> {
	public:
		using vector<Zi>::vector;

		Poly& fix(int sz) {
			resize(sz);
			return *this;
		}
} Poly;

Poly operator + (Poly A, Poly B) {
	int n = A.size(), m = B.size();
	int t = max(n, m);
	A.resize(t), B.resize(t);
	for (int i = 0; i < t; i++) {
		A[i] += B[i];
	}
	return A;
}

Poly operator - (Poly A, Poly B) {
	int n = A.size(), m = B.size();
	int t = max(n, m);
	A.resize(t), B.resize(t);
	for (int i = 0; i < t; i++) {
		A[i] -= B[i];
	}
	return A;
}

Poly sqrt(Poly a) {
	Poly rt (a.size());
	pol_sqrt(a.data(), rt.data(), a.size());
	return rt;
}

Poly operator * (Poly A, Poly B) {
	int n = A.size(), m = B.size();
	int k = NTT.correct_len(n + m - 1);
	if (n < 20 || m < 20) {
		Poly rt (n + m - 1);
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				rt[i + j] += A[i] * B[j];
			}
		}
		return rt;
	}
	A.resize(k), B.resize(k);
	NTT(A.data(), k, 1);
	NTT(B.data(), k, 1);
	for (int i = 0; i < k; i++) {
		A[i] *= B[i];
	}
	NTT(A.data(), k, -1);
	A.resize(n + m - 1);
	return A;
}

Poly operator ~ (Poly f) {
	int n = f.size(), t = NTT.correct_len((n << 1) | 1);
	Poly rt (t);
	f.resize(t);
	pol_inverse(f.data(), rt.data(), n);
	rt.resize(n);
	return rt;
}

Poly operator / (Poly A, Poly B) {
	int n = A.size(), m = B.size();
	if (n < m) {
		return Poly {0};
	}
	int r = n - m + 1;
	reverse(A.begin(), A.end());
	reverse(B.begin(), B.end());
	A.resize(r), B.resize(r);
	A = A * ~B;
	A.resize(r);
	reverse(A.begin(), A.end());
	return A;
}

Poly operator % (Poly A, Poly B) {
	int n = A.size(), m = B.size();
	if (n < m) {
		return A;
	}
	if (m == 1) {
		return Poly {0};
	}
	A = A - A / B * B;
	A.resize(m - 1);
	return A;
}

Zi Inv[N];
void init_inv(int n) {
	Inv[0] = 0, Inv[1] = 1;
	for (int i = 2; i <= n; i++) {
		Inv[i] = Inv[Mod % i] * Zi((Mod - (Mod / i)));
	}
}

void diff(Poly& f) {
	if (f.size() == 1) {
		f[0] = 0;
		return;
	}
	for (int i = 1; i < (signed) f.size(); i++) {
		f[i - 1] = f[i] * Zi(i);
	}
	f.resize(f.size() - 1);
}
void integ(Poly& f) {
	f.resize(f.size() + 1);
	for (int i = (signed) f.size() - 1; i; i--) {
		f[i] = f[i - 1] * Inv[i];
	}
	f[0] = 0;
}

Poly ln(Poly f) {
	int n = f.size();
	Poly h = f;
	diff(h);
	f = h * ~f;
	f.resize(n - 1);
	integ(f);
	return f;
}

void pol_exp(Poly& f, Poly& g, int n) {
	Poly h;
	if (n == 1) {
		g.resize(1);
		g[0] = 1;
	} else {
		int hn = (n + 1) >> 1;
		pol_exp(f, g, hn);
		
		h.resize(n), g.resize(n);
		pcopy(h.data(), h.data() + n, f.data());

		g = g * (Poly{1} - ln(g) + h);
		g.resize(n);
	}
}

Poly exp(Poly f) {
	int n = f.size();
	Poly rt;
	pol_exp(f, rt, n);
	return rt;
}

class PolyBuilder {
	protected:
		int num;
		Poly P[N << 1];
		
		void _init(int *x, int l, int r) {
			if (l == r) {
				P[num++] = Poly{-Zi(x[l]), Zi(1)};
				return;
			}
			int mid = (l + r) >> 1;
			int curid = num++;
			_init(x, l, mid);
			int rid = num;
			_init(x, mid + 1, r);
			P[curid] = P[curid + 1] * P[rid];
		}

		void _evalute(Poly f, Zi* y, int l, int r) {
			f = f % P[num++];
			if (l == r) {
				y[l] = f[0];
				return;
			}
			int mid = (l + r) >> 1;
			_evalute(f, y, l, mid);
			_evalute(f, y, mid + 1, r);
		}
	public:
		Poly evalute(Poly f, int* x, int n) {
			Poly rt(n);
			num = 0;
			_init(x, 0, n - 1);
			num = 0;
			_evalute(f, rt.data(), 0, n - 1);
			return rt;
		}
} PolyBuilder;

int n, m;
int sz[N];
Zi fac[N], _fac[N];

void prepare(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i;
	_fac[n] = ~fac[n];
	for (int i = n; i; i--)
		_fac[i - 1] = _fac[i] * i;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", sz + i);
		m = max(m, sz[i]);
	}

	prepare(m);
	init_inv(m);

	Zi inv2 ((Mod + 1) >> 1);
	Poly A (m + 1);
	for (int i = 0; i <= m; i++) {
		A[i] = qpow(inv2, 1ll * i * (i - 1) / 2 % (Mod - 1)) * _fac[i];
	}
	Poly biG = (A * A).fix(m + 1);
	for (int i = 0; i <= m; i++) {
		biG[i] = biG[i] * qpow(2, 1ll * i * (i - 1) / 2 % (Mod - 1));
	}
	Poly biF = ln(biG) * Poly {inv2};
	
	Poly cmG (m + 1);
	for (int i = 0; i <= m; i++)
		cmG[i] = qpow(2, 1ll * i * (i - 1) / 2 % (Mod - 1)) * _fac[i];
	Poly cmF = ln(cmG);
	Poly cmFn = cmF;
	for (int i = 0; i <= m; i++)
		cmFn[i] *= i;

	for (int i = 0; i <= m; i++)
		A[i] = _fac[i] * qpow(2, 1ll * i * (i - 1) / 2 % (Mod - 1));
	biF[1] = cmF[1] = 0;
	Poly biH = (biF * A).fix(m + 1);
	Poly cmH = (cmF * A).fix(m + 1);
	Poly cmHn = (cmFn * A).fix(m + 1);

	for (int i = 0; i <= m; i++) {
		biH[i] *= fac[i];
		cmH[i] *= fac[i];
		cmHn[i] *= fac[i];
	}
	
	Zi f[2] = {1, 0}, g[2] = {1, 0};
	for (int i = 1; i <= n; i++) {
		int s = sz[i];
		f[1] = f[1] * (biH[s] + cmH[s]) + f[0] * biH[s];
		f[0] = f[0] * (cmH[s] - biH[s]);
		Zi coef = s * qpow(2, 1ll * (s - 1) * (s - 2) / 2 % (Mod - 1));
		g[1] = g[1] * cmHn[s] + g[0] * coef;
		g[0] = g[0] * (cmHn[s] - coef);
	}
	Zi ans = f[1] + f[0] + g[1];
	printf("%d\n", ans.v);
	return 0;
}

Problem E 新年的邀請函

咕咕咕

Problem D(old) 新年的求值

  考慮這樣一個問題 $q_i = a^i$。

  那麼它的答案等於 $f(q_i) = \sum_{j = 0}^n f_j a^{ij}$

  注意到 $ij = \binom{i + j}{2} - \binom{i}{2} - \binom{j}{2}$。所以可以做一次減法卷積。

  原問題的話就用一些簡單換元法可以把問題規約成上面。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

template <typename T>
void pfill(T* pst, const T* ped, T val) {
	for ( ; pst != ped; *(pst++) = val);
}

template <typename T>
void pcopy(T* pst, const T* ped, T* pval) {
	for ( ; pst != ped; *(pst++) = *(pval++));
}

const int N = 1 << 21;
const int Mod = 998244353;
const int bzmax = 23;
const int g = 3;

void exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
	} else {
		exgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
}

int inv(int a, int Mod) {
	int x, y;
	exgcd(a, Mod, x, y);
	return (x < 0) ? (x + Mod) : (x);
}

template <const int Mod = :: Mod>
class Z {
	public:
		int v;

		Z() : v(0) {	}
		Z(int x) : v(x){	}
		Z(ll x) : v(x % Mod) {	}

		friend Z operator + (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
		}
		friend Z operator - (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
		}
		friend Z operator * (const Z& a, const Z& b) {
			return Z(a.v * 1ll * b.v);
		}
		friend Z operator ~ (const Z& a) {
			return inv(a.v, Mod);
		}
		friend Z operator - (const Z& a) {
			return Z(0) - a;
		}
		Z& operator += (Z b) {
			return *this = *this + b;
		}
		Z& operator -= (Z b) {
			return *this = *this - b;
		}
		Z& operator *= (Z b) {
			return *this = *this * b;
		}
		friend boolean operator == (const Z& a, const Z& b) {
			return a.v == b.v;
		} 
};

typedef Z<> Zi;

Zi qpow(Zi a, int p) {
	if (p < Mod - 1)
		p += Mod - 1;
	Zi rt = 1, pa = a;
	for ( ; p; p >>= 1, pa = pa * pa) {
		if (p & 1) {
			rt = rt * pa;
		}
	}
	return rt;
}

const Zi inv2 ((Mod + 1) >> 1);

class NTT {
	private:
		Zi gn[bzmax + 4], _gn[bzmax + 4];
	public:
		
		NTT() {
			for (int i = 0; i <= bzmax; i++) {
				gn[i] = qpow(Zi(g), (Mod - 1) >> i);
				_gn[i] = qpow(Zi(g), -((Mod - 1) >> i));
			}
		}

		void operator () (Zi* f, int len, int sgn) {
			for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) {
				if (i < j)
					swap(f[i], f[j]);
				for (k = len >> 1; k <= j; j -= k, k >>= 1);
			}
			
			Zi *wn = (sgn > 0) ? (gn + 1) : (_gn + 1), w, a, b;
			for (int l = 2, hl; l <= len; l <<= 1, wn++) {
				hl = l >> 1, w = 1;
				for (int i = 0; i < len; i += l, w = 1) {
					for (int j = 0; j < hl; j++, w *= *wn) {
						a = f[i + j], b = f[i + j + hl] * w;
						f[i + j] = a + b;
						f[i + j + hl] = a - b;
					}
				}
			}

			if (sgn < 0) {
				Zi invlen = ~Zi(len);
				for (int i = 0; i < len; i++) {
					f[i] *= invlen;
				}
			}
		}

		int correct_len(int len) {
			int m = 1;
			for ( ; m <= len; m <<= 1);
			return m;
		}
} NTT;

typedef class Poly : public vector<Zi> {
	public:
		using vector<Zi>::vector;

		Poly& fix(int sz) {
			resize(sz);
			return *this;
		}
} Poly;

Poly operator * (Poly A, Poly B) {
	int n = A.size(), m = B.size();
	int k = NTT.correct_len(n + m - 1);
	if (n < 20 || m < 20) {
		Poly rt (n + m - 1);
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				rt[i + j] += A[i] * B[j];
			}
		}
		return rt;
	}
	A.resize(k), B.resize(k);
	NTT(A.data(), k, 1);
	NTT(B.data(), k, 1);
	for (int i = 0; i < k; i++) {
		A[i] *= B[i];
	}
	NTT(A.data(), k, -1);
	A.resize(n + m - 1);
	return A;
}

Zi fac[N], _fac[N];
void prepare(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i;
	_fac[n] = ~fac[n];
	for (int i = n; i; i--)
		_fac[i - 1] = _fac[i] * i;
}

// f'(x) = f(x * d)
void mul(Poly& a, Zi x) {
	Zi pw = 1;
	for (auto& y : a)
		y *= pw, pw *= x;
}

// f'(x) = f(x + d)
Poly poly_mov(Poly f, Zi d) {
	int n = f.size();
	Poly A (n);
	for (int i = 0; i < n; i++)
		f[i] *= fac[i];
	Zi pw = 1;
	for (int i = 0; i < n; i++, pw = pw * d)
		A[i] = _fac[i] * pw;
	reverse(A.begin(), A.end());
	f = f * A;
	for (int i = 0; i < n; i++)
		f[i] = f[i + n - 1] * _fac[i];
	f.resize(n);
	return f;
}

int n, Q;
Poly f;
Zi q0, a, b;

int main() {
	scanf("%d%d", &n, &Q);
	f.resize(++n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &f[i].v);
	}
	scanf("%d%d%d", &q0.v, &a.v, &b.v);
	prepare(n + 1);
	mul(f, ~Zi(a - 1));
	f = poly_mov(f, -b);
	mul(f, q0 * (a - 1) + b);
	++Q;
	Poly A (n + Q - 1);
	A[0] = 1;
	Zi pw = 1;
	for (int i = 1; i < (signed) A.size(); i++) {
		A[i] = A[i - 1] * pw;
		pw *= a;
	}
	pw = 1;
	Zi _a = ~a, pwa = 1;
	for (int i = 1; i < n; i++)
		f[i] *= pwa, pwa *= (pw *= _a);
	reverse(f.begin(), f.end());
	A = A * f;
	unsigned ans = 0;
	pw = pwa = 1;
	for (int i = 1; i < Q; i++) {
		Zi tmp = A[i + n - 1] * pwa;
		pwa *= (pw *= _a);
		ans ^= tmp.v;
	}
	printf("%u\n", ans);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章