GDOI2020 划水记

因为 CSP 挂了 120 分,所以除了这里哪里都没能去...惨惨...

今年虽然就在我们学校,但是我还是只能划水...不像 ntf 和 pb 如果 GDOI 死掉就直接进队了...

Day -2

首先打了一遍可持久化平衡树FHQTreap真的比Splay好写多了

P6577 【模板】二分图最大权完美匹配

大概就是给每个点分配一个标记 (A_i,B_j)。称满足 (A_i+B_jge dis(i,j)) 的为 可行顶标,由 (A_i+B_j=dis(i,j)) 的边构成的子图称为 相等子图。如果相等子图有完美匹配就做完了,所以关键就在于如何分配顶标,顺便用 KM 随便求一种匹配就完事了。

对于一条增广路,计算出一个 (Delta),将左部点顶标减去 (Delta),右部点顶标加上 (Delta)。因为不会有一条边左点不在交错树上,右点在交错树上。于是 (Delta) 只要保证它仍然可行即可,取 (Delta=min(A_i+B_j-dis(i,j)))

由于奇奇怪怪的原因,你需要写 BFS 版 KM。实现上面你还可以假装有一个虚点去匹配,再把虚点删掉就可以空出一个位置...

这东西太神必了...抄完一遍代码之后仍然没怎么弄懂。

int n, m, pre[M], mat[M], a, b;
bool vis[M];
LL ans, d[N][N], w[M], s[M], c;
void bfs(int x){
	memset(vis, 0, sizeof vis);
	memset(pre, 0, sizeof pre);
	memset(s, 0x3f, sizeof s);
	int cur = 0, nxt = 0; mat[0] = x;
	while(mat[cur]){
		x = mat[cur]; vis[cur] = true; LL dis = 1e18;
		for(Rint i = n + 1;i <= (n << 1);++ i) if(!vis[i]){
			LL tmp = w[x] + w[i] - d[x][i - n];
			if(chmin(s[i], tmp)) pre[i] = cur;
			if(chmin(dis, s[i])) nxt = i;
		}
		w[mat[0]] -= dis; w[0] += dis;
		for(Rint i = n + 1;i <= (n << 1);++ i)
			if(vis[i]){w[mat[i]] -= dis; w[i] += dis;}
			else s[i] -= dis;
		cur = nxt;
	}
	while(cur){mat[cur] = mat[pre[cur]]; cur = pre[cur];}
}
int main(){
	read(n); read(m);
	memset(d, -0x3f, sizeof d);
	for(Rint i = 1;i <= m;++ i){
		read(a); read(b); read(c); chmax(d[a][b], c); chmax(w[a], d[a][b]);
	}
	for(Rint i = 1;i <= n;++ i) bfs(i);
	for(Rint i = 1;i <= n;++ i) ans += d[mat[i + n]][i];
	printf("%lld
", ans);
	for(Rint i = n + 1;i <= (n << 1);++ i) printf("%d ", mat[i]);
}

扩展 BSGS

对于 (a^xequiv b( ext{mod} p)),无解当且仅当 (gcd(a,p) ot|b)(b eq 1)

(g=gcd(a,p)),则 (a^{x-1}cdotfrac{a}{g}equivfrac{b}{g}( ext{mod} frac{p}{g})),由于 (gcd(frac{a}{g},frac{p}{g})=1),所以 (a^{x-1}equivfrac{b}{g}(frac{a}{g})^{-1}( ext{mod} frac{p}{g})),递归到了一个 (p) 至少变为原来一半的子问题。直到 (gcd(a,p)=1) 时使用普通 BSGS 来做。

注意在 (p) 不为质数的时候,你不能用 (a^{sqr*i-j}) 来计算而只能用 (a^{sqr*i+j}),因为...因为啥啊...

LL gcd(LL a, LL b){return b ? gcd(b, a % b) : a;}
void exgcd(LL a, LL b, LL &x, LL &y){
	if(!b){x = 1; y = 0; return;}
	exgcd(b, a % b, y, x); y -= a / b * x;
}
LL BSGS(LL a, LL b, LL p){
	unordered_map<LL, int> S; a %= p;
	LL sqr = ceil(sqrt(p)), now = 1, tmp = ksm(a, sqr, p), x, y;
	exgcd(tmp, p, x, y); tmp = (x % p + p) % p;
	for(Rint i = 0;i <= sqr;++ i, now = now * a % p) if(!S.count(now)) S[now] = i;
	now = b;
	for(Rint i = 0;i <= sqr;++ i, now = now * tmp % p) if(S.count(now)) return i * sqr + S[now];
	return -1e9;
}
LL exBSGS(LL a, LL b, LL p){
	LL g, x, y, sd = 1; int k = 0;
	if(b == 1) return 0;
	while((g = gcd(a, p)) > 1){
		if(b % g) return -1; p /= g; ++ k; b /= g;
		sd = a / g * sd % p; if(b == 1) return k;
	}
	exgcd(sd, p, x, y); b = (b * x % p + p) % p;
	return BSGS(a, b, p) + k;
}
int main(){
	LL a, p, b;
	while(true){
		read(a); read(p); read(b);
		if(!a || !p || !b) return 0;
		LL ans = exBSGS(a, b, p); a %= p;
		if(ans < 0) puts("No Solution");
		else printf("%lld
", ans);
	}
}

无源汇上下界可行流

对于每个点计算 (in_x)(out_x) 表示连向 (x) 和连出 (x) 的边的流量下界之和。然后把每条边容量设为上界-下界。设一个虚拟源点和虚拟汇点 (s,t),若 (in_x>out_x) 则连一条 ((s,x,in_x-out_x)),否则连一条 ((x,t,out_x-in_x)),若满流则原图存在可行流。

(就是使用不需要流量平衡的源点和汇点来平衡下界的流量)

有源汇上下界可行流

连一条 ((t,s,+infty)),然后跑无源汇上下界可行流。

有源汇上下界最大流

先跑一遍有源汇上下界最大流,目前的流量是 (flow_1),然后看还能流多少。

此时应该把 ((t,s,+infty)) 删掉,然后在原图源汇上跑一遍最大流,设流量为 (flow_2),则答案为 (flow_1+flow_2)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define PB push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1503, M = 1000000, mod = 998244353;
inline int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()){f |= ch == '-'; if(ch == EOF) exit(0);}
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, m, S, T, deg[N], head[N], to[M], nxt[M], cap[M], cnt, ans, sum;
void add(int a, int b, int c){
	to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; cap[cnt] = c;
	to[++ cnt] = a; nxt[cnt] = head[b]; head[b] = cnt; cap[cnt] = 0;
}
int dep[N], cur[N], q[N], f, r;
bool bfs(){
	memset(dep, -1, sizeof dep);
	memcpy(cur, head, sizeof cur);
	f = r = dep[S] = 0; q[r ++] = S;
	while(f < r){
		int u = q[f ++];
		for(Rint i = head[u];i;i = nxt[i])
			if(dep[to[i]] == -1 && cap[i]){
				dep[to[i]] = dep[u] + 1;
				q[r ++] = to[i];
			}
	}
	return ~dep[T];
}
int dfs(int x, int lim){
	if(!lim || x == T) return lim;
	int flow = 0, f;
	for(Rint i = cur[x];i;i = nxt[i]){
		cur[x] = i;
		if(dep[to[i]] == dep[x] + 1 && (f = dfs(to[i], min(lim, cap[i])))){
			flow += f; lim -= f; cap[i] -= f; cap[i ^ 1] += f; if(!lim) break;
		}
	}
	return flow;
}
int dinic(){int ans = 0; while(bfs()) ans += dfs(S, 1e9); return ans;}
void solve(){
	memset(head, 0, sizeof head); cnt = 1; sum = 0;
	memset(deg, 0, sizeof deg);
	read(n); read(m); S = n + m + 1; T = S + 1;
	for(Rint i = 1, x;i <= m;++ i){read(x); add(n + i, T, 1e9 - x); deg[T] += x; deg[n + i] -= x;}
	for(Rint i = 1, c, d, t, l, r;i <= n;++ i){
		read(c); read(d); add(S, i, d);
		while(c --){
			read(t); read(l); read(r); ++ t;
			add(i, n + t, r - l); deg[i] -= l; deg[n + t] += l;
		}
	} S += 2; T += 2;
	for(Rint i = 1;i <= T - 2;++ i)
		if(deg[i] > 0) add(S, i, deg[i]), sum += deg[i];
		else if(deg[i] < 0) add(i, T, -deg[i]);
	add(T - 2, S - 2, 1e9);
	if(dinic() != sum){puts("-1"); return;}
	ans = cap[cnt]; cap[cnt] = cap[cnt ^ 1] = 0; S -= 2; T -= 2;
	printf("%d
", ans + dinic());
}
int main(){while(true){solve(); putchar('
');}}

有源汇上下界最小流

跟上一个类似,考虑能少流多少流量,就是从原图汇点向源点跑最大流,然后减去这个流量。

ExKMP

啥都别说了,learn by heart.

now = p0 = 0;
while(s[now] == t[now] && now < m) ++ now;
ext[0] = now;
for(Rint i = 1;i < n;++ i){
      if(i + nxt[i - p0] < p0 + ext[p0]) ext[i] = nxt[i - p0];
      else {
            now = max(ext[p0] + p0 - i, 0);
            while(t[now] == s[now + i] && now < m && now + i < n) ++ now;
            ext[i] = now; p0 = i;
      }
}

SA

看同学在做这道题,我就也去尝试了一下...然后调了一个下午还是没有调出来...

最后发现原来只是一个 sb 错误...我人药丸...

#include<bits/stdc++.h>
#define Rint register int
#define PB push_back
using namespace std;
typedef long long LL;
const int N = 222222, M = N * 22, K = M << 1;
template<typename T>
inline void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int T, n, na, nb, m, la[N], ra[N], lb[N], rb[N], rak[N], tmp[N], *x = rak, *y = tmp, sa[N], c[N], ht[18][N], lg2[N];
char s[N];
struct Seg {
	int lb, len, id;
	inline Seg(int _a = 0, int _b = 0, int _c = 0): lb(_a), len(_b), id(_c){}
	inline bool operator < (const Seg &o) const {
		if(len != o.len) return len > o.len; return id < o.id;
	}
} seg[N<<1];
void Qsort(){
	memset(c + 1, 0, m << 2);
	for(Rint i = 1;i <= n;++ i) ++ c[x[y[i]]];
	for(Rint i = 1;i <= m;++ i) c[i] += c[i - 1];
	for(Rint i = n;i;-- i) sa[c[x[y[i]]] --] = y[i];
}
void Ssort(){
	m = 128;
	for(Rint i = 1;i <= n;++ i){
		x[i] = s[i]; y[i] = i;
	}
	Qsort();
	for(Rint w = 1, p;w < n;w <<= 1, m = p, p = 0){
		for(Rint i = n - w + 1;i <= n;++ i) y[++ p] = i;
		for(Rint i = 1;i <= n;++ i) if(sa[i] > w) y[++ p] = sa[i] - w;
		Qsort(); swap(x, y);
		x[sa[1]] = p = 1;
		for(Rint i = 2;i <= n;++ i) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + w] == y[sa[i - 1] + w]) ? p : ++ p;
		if(p >= n) break;
	}
	for(Rint i = 1;i <= n;++ i) rak[sa[i]] = i;
	for(Rint i = 1, k = 0;i <= n;++ i){
		if(k) -- k;
		if(rak[i] == 1) continue;
		int j = sa[rak[i] - 1];
		while(s[i + k] == s[j + k]) ++ k;
		ht[0][rak[i]] = k;
	}
	for(Rint i = 1;i < 18;++ i) // sb 错误
		for(Rint j = 1;j <= n - (1 << i) + 1;++ j)
			ht[i][j] = min(ht[i - 1][j], ht[i - 1][j + (1 << i - 1)]);
}
int cnt, rcnt, rt[N], ls[M], rs[M], d[M], q[M], fr, re, head[M], to[K], nxt[K], ecnt;
LL ans, dp[M];
void add(int u, int v){to[++ ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt; ++ d[v];}
void modify(int &x, int L, int R, int pos, int nod){
	++ cnt; ls[cnt] = ls[x]; rs[cnt] = rs[x]; if(x) add(cnt, x); x = cnt;
	if(L == R){add(x, nod); return;}
	int mid = L + R >> 1;
	if(pos <= mid){modify(ls[x], L, mid, pos, nod); add(x, ls[x]);}
	else {modify(rs[x], mid + 1, R, pos, nod); add(x, rs[x]);}
}
void query(int x, int L, int R, int l, int r, int nod){
	if(!x) return; if(l <= L && R <= r){add(nod, x); return;}
	int mid = L + R >> 1;
	if(l <= mid) query(ls[x], L, mid, l, r, nod);
	if(mid < r) query(rs[x], mid + 1, R, l, r, nod);
}
void solve(){
	scanf("%s", s + 1); n = strlen(s + 1); Ssort(); read(na);
	for(Rint i = 1;i <= na;++ i){
		read(la[i]); read(ra[i]);
		seg[i] = Seg(rak[la[i]], ra[i] - la[i] + 1, i);
	} read(nb);
	for(Rint i = 1;i <= nb;++ i){
		read(lb[i]); read(rb[i]);
		seg[na + i] = Seg(rak[lb[i]], rb[i] - lb[i] + 1, na + i);
	}
	sort(seg + 1, seg + na + nb + 1); cnt = na + nb;
	for(Rint i = 1;i <= na + nb;++ i){
		Seg &p = seg[i];
		if(p.id <= na){
			++ rcnt; modify(rt[rcnt]=rt[rcnt-1], 1, n, p.lb, p.id);
		} else {
			int L = p.lb, R = p.lb;
			for(Rint j = lg2[p.lb - 1];~j;-- j) if(L - (1 << j) >= 1 && ht[j][L - (1 << j) + 1] >= p.len) L -= 1 << j;
			for(Rint j = lg2[n - p.lb];~j;-- j) if(R + (1 << j) <= n && ht[j][R + 1] >= p.len) R += 1 << j;
			query(rt[rcnt], 1, n, L, R, p.id);
		}
	}
	read(m); while(m --){int x, y; read(x); read(y); add(x, y + na);}
	for(Rint i = 1;i <= cnt;++ i) if(!d[i]){q[re ++] = i; if(i <= na) dp[i] = ra[i] - la[i] + 1;}
	while(fr < re){
		int u = q[fr ++]; chmax(ans, dp[u]);
		for(Rint i = head[u];i;i = nxt[i]){
			int &v = to[i]; chmax(dp[v], dp[u]);
			if(!-- d[v]){q[re ++] = v; if(v <= na) dp[v] += ra[v] - la[v] + 1;}
		}
	}
	if(re == cnt) printf("%lld
", ans); else puts("-1");
	for(Rint i = 1;i <= cnt;++ i) d[i] = dp[i] = ls[i] = rs[i] = head[i] = 0;
	for(Rint i = 1;i <= rcnt;++ i) rt[i] = 0; fr = re = rcnt = ans = ecnt = 0;
}
int main(){
    read(T); lg2[0] = -1;
	for(Rint i = 1;i < N;++ i) lg2[i] = lg2[i >> 1] + 1;
	while(T --) solve();
}

Day -1

早上打模拟赛被打自闭了...

点分树

普通的点分树用来维护一些路径相关的东西...通过在点分中心统计贡献来降低复杂度...

点分树可以动态加点,像替罪羊树一样在某一个子树过大的时候暴力重构...紫荆花之恋比较自闭,就写了 [WC2018] 即时战略

#include<bits/stdc++.h>
#include"rts.h"
#define Rint register int
using namespace std;
const int N = 300003;
bool vis[N];
int a[N];
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
void workLine(int n){
	for(Rint i = 1;i < n;++ i) a[i] = i + 1;
	shuffle(a + 1, a + n, rng); vis[1] = 1;
	int l = 1, r = 1;
	for(Rint i = 1;i < n;++ i) if(!vis[a[i]]){
		int to = a[i], now, x = explore(l, to);
		if(!vis[x]){vis[x] = true; now = x; l = to;}
		else {now = r; r = to;}
		while(!vis[to]) vis[now = explore(now, to)] = true;
	}
}
int rt, siz[N], dsiz[N], ddep[N], dfa[N], wson[N], head[N], to[N << 1], nxt[N << 1];
bool rch[N];
void add(int a, int b){static int cnt = 0; to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int find(int x, int f, int tot){
	siz[x] = 1; wson[x] = 0; int cen = 0;
	for(Rint i = head[x];i;i = nxt[i])
		if(to[i] != f && !vis[to[i]]){
			int tmp = find(to[i], x, tot);
			chmax(wson[x], siz[to[i]]); siz[x] += siz[to[i]];
			if(!cen || wson[tmp] < wson[cen]) cen = tmp;
		}
	chmax(wson[x], tot - siz[x]);
	if(!cen || wson[x] < wson[cen]) cen = x; return cen;
}
void clear(int x, int f, int dep){
	ddep[x] = dfa[x] = dsiz[x] = vis[x] = 0;
	for(Rint i = head[x];i;i = nxt[i])
		if(to[i] != f && ddep[to[i]] >= dep)
			clear(to[i], x, dep);
}
void divide(int x){
	vis[x] = true;
	for(Rint i = head[x];i;i = nxt[i])
		if(!vis[to[i]]){
			int tmp = siz[x] < siz[to[i]] ? dsiz[x] - siz[x] : siz[to[i]], cen = find(to[i], 0, tmp);
			dfa[cen] = x; ddep[cen] = ddep[x] + 1; dsiz[cen] = tmp; divide(cen);
		}
}
void rebuild(int x){
	int dep = ddep[x], tot = dsiz[x], fa = dfa[x];
	clear(x, 0, dep); int cen = find(x, 0, tot);
	ddep[cen] = dep; dsiz[cen] = tot; dfa[cen] = fa;
	if(rt == x) rt = cen; divide(cen);
}
void reach(int to){
	int now = rt;
	while(!rch[to]){
		int nxt = explore(now, to);
		if(rch[nxt]){
			while(dfa[nxt] != now) nxt = dfa[nxt];
		} else {
			rch[nxt] = vis[nxt] = true; dsiz[nxt] = 0; ddep[nxt] = ddep[now] + 1; dfa[nxt] = now;
			for(Rint i = nxt;i;i = dfa[i]) ++ dsiz[i]; add(now, nxt); add(nxt, now);
			int lst = 0;
			for(Rint i = nxt;dfa[i];i = dfa[i]) if(dsiz[i] > 0.7 * dsiz[dfa[i]]) lst = dfa[i];
			if(lst) rebuild(lst);
		} now = nxt;
	}
}
void play(int n, int T, int dataType){
	if(dataType == 3){workLine(n); return;}
	rch[rt = 1] = vis[1] = true;
	for(Rint i = 1;i < n;++ i) a[i] = i + 1;
	shuffle(a + 1, a + n, rng);
	for(Rint i = 1;i < n;++ i) reach(a[i]);
}

Day 0

没有模拟赛,本来准备开昨天 Global Round 8 的 VP,也咕咕咕了。

一个上午光忙着换机房了...什么题都没做...

下午搞了几局扫雷,晚上和其他大佬做了一些 CF 低等难度信心题。同年级不参加省选的人都去旅游了...我(e^2)...

写了一遍 LCT 模板,虽然没有一次写对但是总归是 <30min 了,NTF 还蜜汁 TLE,我人傻了:(

Day 1

7:43 am

晚上还是一如既往的没睡好,早上起来吃完饭来到信息楼,发现加了一些封条和栅栏就直接绕过去了

来到401腐败,还是自闭,啥都不想写...看看之前做过的题吧...

转发一个 SF 神仙做的省选地图

GD 以前这么自闭的都联考了,ZJ 和 FJ 还要坚持出自己的毒瘤题Nice-a

8:30 a.m.

先写了个板子。

8:36 a.m.

看题目。

T1 看上去比较复杂,先放着。

T2 题目背景是小葱,极度熟悉你还可以在博客里面找到一道极其类似的题的题解

9:02 a.m.

过 T2 大样例。

10:35 a.m.

写完 T1 开始拍。

因为我码力 tcl,所以本来想着要卡常就写了树状数组,后来写着写着烦的要死就直接上了个 map...

11:07 a.m.

拍上了(每次要么暴力写挂要么数据生成器写挂),测了速发现被卡常了...以为是少爷机就可以过去,但是出来才知道不开 O2,喜提 60 分

0:35 p.m.

T3 想自闭了,来写暴力。

Day 2

8:30 a.m.

本来想着进考场先写个板子,然而写到一半被叫停了...

开场看了个题,T1 好像是卡常的状压dp,T2,T3没什么思路

9:57 a.m.

写完了 T1,测了速跑了 3s,也不知道少爷机能不能跑过去。

T2 只会分位然后根号分治,大概能搞 40 分。

T3 直接各个部分分写就有 70 分了。

0:12 p.m.

该搞的差不多都搞完了,自闭了

Day 4

估分 (60+100+15+100+40+70=385),实际分数 (60+100+15+80+40+60=355),D2T1 被卡常了,D2T3 各个部分分写,可能就写挂了一点。

总而言之还是十分丢人的,D2T2 和 D2T3 还是见过的 trick,但考场上面一自闭就想不出来了...考场上面大部分思路还是来源于第一印象,这就直接暴毙了。。。

还是做题不够多,D2T2 ntf 见过两次了,我还看过一次题解,好像还没改...

PB 拿了 B 类 rk1,NTF 压线进队不可能是评测挂了,ntf申诉失败,QWS 被学姐吊打了,我差亿点点就进去了,还是预料之中吧

原文地址:https://www.cnblogs.com/AThousandMoons/p/13154101.html