GDOI2020 划水记

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

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

Day -2

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

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

大概就是给每个点分配一个标记 \(A_i,B_j\)。称满足 \(A_i+B_j\ge 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\n", ans);
	for(Rint i = n + 1;i <= (n << 1);++ i) printf("%d ", mat[i]);
}

扩展 BSGS

对于 \(a^x\equiv b(\text{mod} \ p)\),无解当且仅当 \(\gcd(a,p)\not|b\)\(b\neq 1\)

\(g=\gcd(a,p)\),则 \(a^{x-1}\cdot\frac{a}{g}\equiv\frac{b}{g}(\text{mod} \ \frac{p}{g})\),由于 \(\gcd(\frac{a}{g},\frac{p}{g})=1\),所以 \(a^{x-1}\equiv\frac{b}{g}(\frac{a}{g})^{-1}(\text{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 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\n", 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
#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 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
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
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template
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\n", ans + dinic());
}
int main(){while(true){solve(); putchar('\n');}}

有源汇上下界最小流

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

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
#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
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
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\n", 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
#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
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,也咕咕咕了。

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

数竞室的电脑实在垃圾(虽然是一体机已经挺好的了)只能玩 cat,还发现有个人玩 cat 比 NTF 还疯狂...

我还机惨了一个人把他的 360 危险浏览器快捷方式换成了 Chrome Canary 2333

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

写了一遍 LCT 模板,虽然没有一次写对但是总归是 <30min 了,NTF 还蜜汁 TLE,我人傻了,估计他省选码力要起飞(字面意思)

Day 1

7:43 am

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

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

转发一个 SF 神仙做的省选地图 GDOI2020 划水记_第1张图片

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

2:03 p.m

我天真地以为草稿纸不会收,于是把游记写在草稿纸上了。然后我就只能 Quine 了(游记套游记)

8:30 a.m.

先写了个板子。

8:36 a.m.

看题目。

9:02 a.m.

过 T2 大样例。

10:35 a.m.

写完 T1 开始拍。

11:07 a.m.

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

0:35 p.m.

T3 想自闭了,来写暴力。

0:57 p.m.

我人傻了,感到极度迷茫,明天要是毒瘤一点我就直接被打爆了...

2:00 p.m.

回到宿舍腐败+睡觉...


下午又到数竞室腐败,复习了一下 Cat,给一个电脑装 Ubuntu 20.04 失败了反正本来就是废的

晚上玩 DQ 极其擅长的对抗 Tetris,感觉被打爆了。

听同学说还有一场 AGC,但是到了 8:00 题目看不到...自闭了...

你可能感兴趣的:(GDOI2020 划水记)