因为 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腐败,还是自闭,啥都不想写...看看之前做过的题吧...
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 题目看不到...自闭了...