正睿 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,改不了一点。

浙公网安备 33010602011771号