zoukankan      html  css  js  c++  java
  • 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
    ", 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(nlog^2 V)$  的 dp 做法。

      冷静一下仔细分析发现,每个状态 $[l, r]$ 一定满足要么 $l = 1$ 或者 $r = n$,要么 $r$ 是从 $l$ 开始的前缀 gcd 发生变化的位置,要么 $l$ 是从 $r$ 开始的后缀 gcd 发生变化的位置。因此状态数为 $O(nlog 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
    ", 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
    ", 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 = inom{i + j}{2} - inom{i}{2} - inom{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
    ", ans);
    	return 0;
    }

  • 相关阅读:
    windows下运行命令行mysql,提示mysql不是内部命令,解决办法
    XML和HTML的区别
    BZOJ4695 最假女选手(势能线段树)
    BZOJ5312 冒险(势能线段树)
    洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)
    区间子集最大/最小异或和问题(线性基,树上差分)
    线性基模板(线性基)
    分数模板(C++模板)
    洛谷P2516 [HAOI2010]最长公共子序列(LCS,最短路)
    组合数学知识要点
  • 原文地址:https://www.cnblogs.com/yyf0309/p/12235646.html
Copyright © 2011-2022 走看看