正睿 25 年省选联考 Day 6

正睿 25 年省选联考 Day 6

得分

T1 T2 T3 总分 排名
\(0\) \(20\) \(15\) \(35\) \(26/33\)
  • T1 暴力没有判断出点是否被删除,\(35\to 0\)
  • T3 组合数忘记取模,\(25\to 15\)

题解

T1 涩涩的图

显然贡献的两个点必然在同一个边双里,考虑删边改加边,然后我们就需要动态维护边双信息。可以考虑使用 LCT 维护边双,然后用并查集维护每一种颜色的个数,合并时采用启发式合并,复杂度就是 \(O(n\log^2 n)\)\(O(n\log n)\),视实现细节决定。

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int Maxn = 5e5 + 5;
const int Inf = 2e9;

int n, m, k, q;
int c[Maxn];
vector <int> E[Maxn];
int del[Maxn], nd[Maxn];

struct Edge {
	int u, v;
}e[Maxn];

int ans = 0;
namespace DSU {
	int fa[Maxn];
	unordered_map <int, int> mp[Maxn];
	void init() {for(int i = 1; i <= n; i++) fa[i] = i, mp[i][c[i]] = 1;}
	int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
	void merge(int x, int y) {
		x = find(x), y = find(y);
		if(x == y) return ;
		if(mp[x].size() > mp[y].size()) swap(x, y);
		fa[x] = y;
		for(auto i : mp[x]) {
			int num1 = i.second, num2 = mp[y][i.first];
			ans += -num1 * (num1 - 1) / 2 - num2 * (num2 - 1) / 2 + (num1 + num2) * (num1 + num2 - 1) / 2;
			mp[y][i.first] += i.second;
		}
		mp[x].clear();
	}
}

namespace LCT {
	struct node {
		int fa, son[2], tag;
	}t[Maxn];
	#define ls(p) t[p].son[0]
	#define rs(p) t[p].son[1]
	#define fa(p) DSU::find(t[p].fa)
	#define tag(p) t[p].tag
	int get(int p) {return rs(fa(p)) == p;}
	int isroot(int p) {return ls(fa(p)) != p && rs(fa(p)) != p;}
	void pushrev(int p) {swap(ls(p), rs(p)), tag(p) ^= 1;}
	void pushdown(int p) {if(tag(p)) pushrev(ls(p)), pushrev(rs(p)), tag(p) = 0;}
	void update(int p) {
		if(!isroot(p)) update(fa(p));
		pushdown(p);
	}
	void rotate(int p) {
		int y = fa(p), z = fa(y), c = get(p);
		if(!isroot(y)) t[z].son[get(y)] = p;
		t[t[p].son[c ^ 1]].fa = y;
		t[y].son[c] = t[p].son[c ^ 1];
		t[p].son[c ^ 1] = y;
		t[y].fa = p, t[p].fa = z; 
	}
	void splay(int p) {
		update(p);
		int f = fa(p);
		while(!isroot(p)) {
			if(!isroot(f)) {
				rotate(get(f) == get(p) ? f : p);
			}
			rotate(p);
			f = fa(p);
		}
	}
	void access(int p) {
		int x = 0;
		while(p) {
			splay(p), rs(p) = x;
			x = p, p = fa(p);
		}
	}
	void makeroot(int p) {
		access(p), splay(p);
		pushrev(p);
	}
	void split(int x, int y) {
		makeroot(x), access(y), splay(y);
	}
	int find(int p) {
		access(p);
		splay(p);
		while(ls(p)) {
			pushdown(p); p = ls(p);
		}
		splay(p);
		return p;
	}
	void dfs(int p, int rt) {
		DSU::merge(p, rt);
		pushdown(p);
		if(ls(p)) dfs(ls(p), rt);
		if(rs(p)) dfs(rs(p), rt);
	}
	void link(int x, int y) {
		x = DSU::find(x), y = DSU::find(y);
		if(find(x) == find(y)) {
			split(x, y);
			dfs(y, y); 
			int rt = DSU::find(y);
			t[rt].fa = t[y].fa;
			ls(rt) = rs(rt) = 0;
		}
		else {
			makeroot(x);
			t[x].fa = y;
		}	
	}
}

int res[Maxn];
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> k >> q;
	for(int i = 1; i <= n; i++) cin >> c[i];
	for(int i = 1; i <= m; i++) {
		int u, v; cin >> u >> v;
		e[i] = {u, v};
		E[u].push_back(v), E[v].push_back(u);
	}
	for(int i = 1; i <= q; i++) {
		int x; cin >> x;
		if(del[x]) continue;
		del[x] = 1, nd[i] = x;
	}
	DSU::init();
	for(int i = 1; i <= m; i++) {
		if(!del[e[i].u] && !del[e[i].v]) {
			LCT::link(e[i].u, e[i].v);
		}
	}
	res[q + 1] = ans;
	for(int i = q; i >= 1; i--) {
		if(!nd[i]) {
			res[i] = ans; continue;
		}
		for(auto to : E[nd[i]]) {
			if(del[to]) continue;
			LCT::link(nd[i], to);
		}
		del[nd[i]] = 0;
		res[i] = ans;
	}
	for(int i = 1; i <= q + 1; i++) cout << res[i] << '\n';
	return 0;
}

T2 因式寄数

首先有两个引理:

  • \(f^2(x)\equiv f(x^2) \pmod 2\)。证明显然。
  • \(\gcd(f,f')\)\(f\) 的最大平方因子。证明的话考虑设 \(f(x)=g^2(x)h(x)\),求导后 \(f'(x)=g^2(x)h'(x)+2g(x)h(x)g'(x)\),后半部分是 \(2\) 的倍数不管,现在只需要证明 \(h(x)\)\(h'(x)\) 是否互质即可。这个也不难证明,如果不互质必然有公因式 \(p(x)\),求导对比后发现这要求 \(p(x)\)\(p'(x)\) 也不互质,否则 \(h(x)\) 中也有平方因子;而根据无穷递降可以知道,这不可能一直成立,所以不可能不互质。

然后根据 Hint,我们就有一个基本的思路:将 \(f(x)\) 拆成平方因子和非平方因子,平方因子开方后继续递归,只需要处理剩下部分。

非平方因子的处理继续考虑用 Hint,我们找出一个 \(h^2(x)\equiv h(x)\pmod f\)。根据引理 \(1\)\(h^2(x)\equiv h(x^2)\equiv h(x)\pmod f\)。我们可以设出 \(h(x)\) 的各项系数,然后这就转化成了一个线性异或方程组,高斯消元暴力计算即可。如果 \(h\) 有非平凡解(不为 \(0,1\)),则由于 \(f\mid h(h-1)\),且 \(h,h-1\) 不互质,那么 \(f=\gcd(f,h)\times \gcd(f,h-1)\),对二者继续递归求解即可。

注意高斯消元中对自由元的处理,为了尽可能出现非平凡解要赋成 \(1\)

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int Maxn = 2e3 + 5;
const int Inf = 2e9;
const int Mod = 998244353;

int T, n;
struct Poly {
	bitset <Maxn> c;
	int len;//次数 项数减一 
	Poly() {c.reset(), len = 0;}
	Poly(int n) {c.reset(), len = n;}
	void flatten() {
		while(len > 0 && !c[len]) len--; 
	}
	Poly& operator += (const Poly &b) {
		len = max(len, b.len);
		c ^= b.c;
		flatten();
		return *this;
	}
	Poly operator + (const Poly &b) const {
		Poly p = *this; p += b;
		return p; 
	}
	Poly& operator %= (const Poly &b) {
		for(int i = len; i >= b.len; i--) {
			if(c[i]) {
				c ^= (b.c << (i - b.len));
			}
		}
		flatten();
		return *this;
	}
	Poly operator % (const Poly &b) const {
		Poly p = *this; p %= b;
		return p; 
	}
	Poly operator / (const Poly &b) const {
		Poly res(len - b.len), p = *this;
		for(int i = len; i >= b.len; i--) {
			if(p.c[i]) {
				p.c ^= (b.c << (i - b.len));
				res.c[i - b.len] = 1;
			}
		}
		res.flatten();
		return res;
	}
	Poly sqrt() {
		Poly res(len >> 1);
		for(int i = 0; i <= len; i++) {
			if(!(i & 1)) res.c[i >> 1] = c[i];
		}
		res.flatten();
		return res;
	}
	Poly dao() {
		Poly res(len);
		for(int i = 0; i <= len; i++) {
			if(i & 1) res.c[i - 1] = c[i];
		}
		res.flatten();
		return res;
	}
	void print() {
		for(int i = 0; i <= len; i++) {
			cout << c[i] << " ";
		}
		cout << '\n';
	}
};

Poly gcd(Poly a, Poly b) {
	if(b.len == 0 && b.c[0] == 0) return a;
	return gcd(b, a % b);
}

unordered_map <bitset<Maxn>, int> res;
Poly mat[Maxn];

void gauss(int n) {
	for(int i = 0; i <= n; i++) {
		int u = i;
		for(int j = 0; j <= n; j++) {
			if(mat[j].c[j] > 0 && j < i) continue;
			if(mat[j].c[i] > mat[u].c[i]) {
				u = j;
			}
		}
		swap(mat[i], mat[u]);
		if(mat[i].c[i] == 0) continue;
		for(int j = 0; j <= n; j++) {
			if(i != j && mat[j].c[i] == 1) {
				mat[j].c ^= mat[i].c;
			}
		}
	}
	for(int i = n; i >= 0; i--) {
		if(mat[i].c.none()) mat[i].c[n + 1] = 1;
		else {
			for(int j = i + 1; j <= n; j++) {
				if(mat[i].c[j] && mat[j].c[n + 1]) mat[i].c.flip(n + 1);
			}
		}
	}
}

void solve2(Poly F, int num) {
	if(F.len == 0) return ;
	for(int i = 0; i < F.len; i++) {
		mat[i].c.reset(), mat[i].len = F.len;
		mat[i].c[i] = 1;
	}
	Poly mul(F.len);
	mul.c[0] = 1;
	for(int i = 0; i <= (F.len - 1) << 1; i++) {
		if(!(i & 1)) {
			for(int j = 0; j < F.len; j++) {
				if(mul.c[j]) mat[j].c.flip(i >> 1);
			}
		}
		mul.c <<= 1, mul.len++;
		mul %= F;
	}
	for(int i = 0; i < F.len; i++) mat[i].print();
	gauss(F.len - 1);
	for(int i = 0; i < F.len; i++) mat[i].print();
	Poly h(F.len);
	for(int i = 0; i < F.len; i++) {
		h.c[i] = mat[i].c[F.len];
	}
	h.flatten();	
	if(h.len == 0) {
		res[F.c] += num; return ;
	}
	solve2(gcd(F, h), num);
	h.c.flip(0);
	solve2(gcd(F, h), num);
}

void solve1(Poly F, int num) {
	if(F.len == 0) return ;
	Poly g = gcd(F, F.dao());
	solve2(F / g, num);
	if(g.len != 0) {
		solve1(g.sqrt(), num * 2);
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> T >> n;
	Poly F(n);
	for(int i = 0; i <= n; i++) {
		int v; cin >> v;
		F.c[i] = v;
	}
	solve1(F, 1);
	int ans = 1;
	for(auto i : res) {
		ans = ans * (i.second + 1) % Mod;
	}
	cout << ans << '\n';
	return 0;
}

T3 高维空间

需要用到 LGV 引理和分治 NTT,改不了一点。

posted @ 2025-02-17 20:47  UKE_Automation  阅读(68)  评论(2)    收藏  举报