20241028
A.鐵路 2
題意簡述
給一棵樹,每次跳一條簡單路徑,定義 \(f(x,y)=\min(\texttt{x到y經過的簡單路徑長度})\)
求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^n f(i,j)\)
思路
注意到,一定存在到直徑端點的方案,找到直徑,搜尋即可
Code
code
const int N = 5e5 + 10;
int n;
vector<pair<int, ll>> g[N];
ll dep[N];
ll rt;
ll pt;
ll dis[N];
void dfs(int x, int fa)
{
if (dep[x] > dep[rt])
{
rt = x;
}
for (auto [v, w] : g[x])
{
if (v == fa)
continue;
dep[v] = dep[x] + w;
dfs(v, x);
}
}
void dfss(int x, int fa)
{
for (auto [v, w] : g[x])
{
if (v == fa)
continue;
dep[v] = dep[x] + w;
dfss(v, x);
}
}
int solve()
{
dfs(1, 0);
pt = rt;
mms(dep, 0);
dfs(pt, 0);
mms(dep, 0);
dfss(rt, 0);
memcpy(dis, dep, sizeof(dep));
mms(dep, 0);
dfss(pt, 0);
rep(i, 1, n) chkmx(dis[i], dep[i]);
sort(dis + 1, dis + n + 1);
ll res = 0;
debug(rt, pt);
rep(i, 1, n)
{
res = 1ll * (1ll * res + 1ll * dis[i] % MOD * (n - i) % MOD) % MOD;
debug(dis[i]);
}
// cout << res * 2 % MOD << endl;
return 1ll * res * 2ll % MOD;
}
int travel(vector<int> U, vector<int> V, vector<int> W)
{
n = U.size() + 1;
rep(i, 0, n - 2)
{
U[i]++;
V[i]++;
g[U[i]].push_back({V[i], W[i]});
g[V[i]].push_back({U[i], W[i]});
}
return solve();
}
警察與小偷
https://www.luogu.com.cn/problem/P11237
分討題,如果 \(V1\leq V2\),那麼一定在根追上,否則二分小偷能向上最大的距離,然後算一下在葉子最優,還是在路徑上被追上最優
Code
struct Frac
{
int a, b;
bool operator<(const Frac &other) const
{
return (__int128_t)a * other.b < (__int128_t)b * other.a;
}
bool operator<=(const Frac &other) const
{
return (__int128_t)a * other.b <= (__int128_t)b * other.a;
}
};
int n, q;
vector<pii> g[N];
struct query
{
int p, v1, t, v2;
} qry[N];
int dep[N], depw[N], rec[N][20];
int fawt[N];
int st[N], en[N], dfntot = 0;
int mx[N], ex[N];
void dfs(int u, int fa)
{
st[u] = ++dfntot;
rec[u][0] = fa;
rep(i, 1, 19)
rec[u][i] = rec[rec[u][i - 1]][i - 1];
for (auto [v, w] : g[u])
{
if (v == fa)
continue;
depw[v] = depw[u] + w;
fawt[v] = w;
dep[v] = dep[u] + 1;
dfs(v, u);
chkmx(mx[u], mx[v] + w);
}
en[u] = dfntot;
}
void dfs2(int u, int fa)
{
int cur = ex[u] + fawt[u];
for (auto [v, w] : g[u])
{
if (v == fa)
continue;
chkmx(ex[v], cur);
chkmx(cur, mx[v] + w);
}
reverse(ALL(g[u]));
cur = 0;
for (auto [v, w] : g[u])
{
if (v == fa)
continue;
chkmx(ex[v], cur);
chkmx(cur, mx[v] + w);
}
for (auto [v, w] : g[u])
{
if (v == fa)
continue;
dfs2(v, u);
}
}
int jump(int u, int k)
{
per(i, 19, 0)
{
if (k & (1 << i))
u = rec[u][i];
}
return u;
}
int getlca(int u, int v)
{
if (dep[u] < dep[v])
swap(u, v);
per(i, 19, 0) if (dep[rec[u][i]] >= dep[v]) u = rec[u][i];
if (u == v)
return u;
per(i, 19, 0)
{
if (rec[u][i] != rec[v][i])
{
u = rec[u][i];
v = rec[v][i];
}
}
return rec[u][0];
}
int p, v1, t, v2, lca, len, lflen;
int goid(int x)
{
if (x <= lflen)
return jump(t, x);
else
return jump(p, len - x);
}
int getlf(int x, int v)
{
if (st[x] <= st[v] && en[x] >= st[v])
return ex[jump(v, dep[v] - dep[x] - 1)];
return mx[x];
}
int check(int id)
{
if (id > len)
return 0;
int x = goid(id);
int tdis = id <= lflen ? depw[t] - depw[x] : depw[x] + depw[t] - depw[lca] * 2;
int pdis = id <= lflen ? depw[x] + depw[p] - depw[lca] * 2 : depw[p] - depw[x];
int dis = getlf(x, p);
// debug(id, pdis, v1, tdis, v2);
if ((Frac){
pdis, v1} <= (Frac){
tdis, v2})
return 0;
return ((Frac){
tdis + dis, v2} < (Frac){
pdis + dis, v1})
? 2
: 1;
}
int getdis(int x, int y)
{
return depw[x] + depw[y] - depw[getlca(x, y)] * 2;
}
vector<array<long long, 2>> police_thief(vector<signed> A, vector<signed> B, vector<signed> D,
vector<signed> P, vector<signed> V1, vector<signed> T, vector<signed> V2)
{
n = A.size() + 1;
q = P.size();
rep(i, 0, n - 2)
{
int u = A[i], v = B[i], w = D[i];
u++;
v++;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
rep(i, 0, q - 1)
{
int p, v1, t, v2;
p = P[i] + 1, v1 = V1[i], t = T[i] + 1, v2 = V2[i];
qry[i + 1] = {p, v1, t, v2};
}
dfs(1, 0);
dfs2(1, 0);
vector<array<long long, 2>> ans(q);
rep(i, 1, q)
{
p = qry[i].p, v1 = qry[i].v1, t = qry[i].t, v2 = qry[i].v2;
lca = getlca(p, t);
debug(p, t, lca);
len = dep[p] + dep[t] - 2 * dep[lca];
lflen = dep[t] - dep[lca];
Frac res = {0, 1};
if (check(0) < 2)
{
res = {depw[p] + depw[t] - depw[lca] * 2, (v1 - v2)};
}
else
{
int l = 0, r = len;
while (l + 1 < r)
{
int mid = (l + r) / 2;
if (check(mid) == 2)
l = mid;
else
r = mid;
}
int x = goid(l);
res = {getdis(p, x) + getlf(x, p), v1};
debug(l, lflen, x, res.a, res.b);
x = goid(l + 1);
debug(x, res.a, res.b);
if (check(l + 1))
res = max(res, {getdis(p, x) - getdis(t, x), v1 - v2});
}
ans[i - 1] = {res.a, res.b};
}
return ans;
}
場上畏懼分討而沒有繼續做,其實討論的類不多,但是維護比較煩
20241029
島嶼
https://www.luogu.com.cn/problem/P11252
觀察邊數量的性質,一定出現一個連線 \((x-1,x+1)\) 的邊,找到,然後找一種染色方案後 bfs 確定其它的染色方案
跳躍遊戲
https://www.luogu.com.cn/problem/P11239
等價於選擇一些距離 \(\geq K\) 的權值的和最大
考慮用線段樹維護當前的 \(K\) 個,如何轉移
- 直接保留原來的值
- 從前面轉移過來,直接取字首最大值,然後區間修改,全域性max即可
Code
int n, q, k;
signed rt = 1;
struct segment_tree
{
struct node
{
signed ls, rs;
int mx, lz;
node() : ls(0), rs(0), mx(0), lz(0) {}
void tag(int x)
{
mx += x;
lz += x;
}
} tr[N << 6];
int tot = 1;
void pushup(int p)
{
tr[p].mx = max(tr[tr[p].ls].mx, tr[tr[p].rs].mx);
}
void pushdown(int p)
{
if (tr[p].lz)
{
if (!tr[p].ls)
tr[p].ls = ++tot;
tr[tr[p].ls].tag(tr[p].lz);
if (!tr[p].rs)
tr[p].rs = ++tot;
tr[tr[p].rs].tag(tr[p].lz);
tr[p].lz = 0;
}
}
void upd(signed &p, int l, int r, int ll, int rr, int val)
{
// debug(ll, rr);
if (ll > rr)
return;
if (!p)
p = ++tot;
if (ll <= l && r <= rr)
{
// debug("flag", l, r, val);
tr[p].tag(val);
return;
}
pushdown(p);
int mid = l + r >> 1;
if (mid >= ll)
upd(tr[p].ls, l, mid, ll, rr, val);
if (mid < rr)
upd(tr[p].rs, mid + 1, r, ll, rr, val);
pushup(p);
}
void upd(signed &p, int l, int r, int pos, int val)
{
if (!p)
p = ++tot;
if (l == r)
{
chkmx(tr[p].mx, val);
return;
}
pushdown(p);
int mid = l + r >> 1;
if (pos <= mid)
upd(tr[p].ls, l, mid, pos, val);
else
upd(tr[p].rs, mid + 1, r, pos, val);
pushup(p);
}
} seg;
void upd(int pos, int val)
{
// debug(pos, pos / k, val);
seg.tr[rt].tag((pos / k) * val);
seg.upd(rt, 0, k - 1, 0, pos % k, val);
}
struct segt
{
int l, r;
int val;
};
vector<segt> vec;
map<int, signed> mp;
int sum[N];
pii rg[N];
int dp[N];
long long play_game(long long N, signed Q, long long K, vector<long long> L, vector<long long> R)
{
n = N;
q = Q;
k = K;
// debug(n, q, k);
mp[1] = 0;
mp[n + 1] = 0;
rep(i, 0, q - 1)
{
rg[i + 1] = {L[i] + 1, R[i] + 1};
mp[L[i] + 1]++;
mp[R[i] + 2]--;
}
int pres = 0;
for (auto it = mp.begin(); it != mp.end(); it++)
{
if ((*it).first == n + 1)
break;
pres += (*it).second;
vec.push_back({(*it).first, (*next(it)).first - 1, pres});
}
// for (auto [l, r, val] : vec)
// debug(l, r, val);
int cnt = 0;
for (auto [l, r, val] : vec)
{
cnt++;
upd(r, val);
upd(l - 1, -val);
dp[cnt] = seg.tr[rt].mx;
// debug(cnt, dp[cnt]);
if (dp[cnt] != dp[cnt - 1])
seg.upd(rt, 0, k - 1, (r + 1) % k, dp[cnt] - val);
}
return dp[cnt];
}
病毒
https://www.luogu.com.cn/problem/P11241
建點分樹
在點分樹
上刻畫題目原來的條件
然後對於每個點 \(u\),所有 \(u\) 子樹內的 \(v\) 按照當前的深度建一個 \(V_{dep}\) 表示深度為 \(dep\) 的點,建 \(V_{dep}\rightarrow V_{dep-1}\) 邊
對於每個人向 \(V_{D_v-dep_v}\) 建邊,
然後跑 Dijkstra 即可
Code
int n, m, k;
vector<int> g[N];
vector<pii> grp[N], val[N];
bool vis[N];
int cst[N];
int cnt = 0;
void dfs(int x, int fa)
{
cnt++;
for (auto v : g[x])
{
if (v == fa || vis[v])
continue;
dfs(v, x);
}
}
int siz[N], mx[N], rt;
void dfs2(int x, int fa)
{
siz[x] = 1;
mx[x] = 0;
for (auto v : g[x])
{
if (v == fa || vis[v])
continue;
dfs2(v, x);
siz[x] += siz[v];
chkmx(mx[x], siz[v]);
}
chkmx(mx[x], cnt - siz[x]);
if (mx[x] < mx[rt])
rt = x;
}
int pre[N], suf[N], tot;
int cur = 0;
void dfs3(int x, int fa, int dep)
{
if (dep > cur)
{
cur++;
pre[cur] = ++tot;
if (cur)
grp[pre[cur]].push_back({pre[cur - 1], 0});
suf[cur] = ++tot;
if (cur)
grp[suf[cur - 1]].push_back({suf[cur], 0});
}
grp[pre[dep]].push_back({m + x, cst[x]});
grp[m + x].push_back({suf[dep], 0});
for (auto v : g[x])
{
if (v == fa || vis[v])
continue;
dfs3(v, x, dep + 1);
}
}
void dfs4(int x, int fa, int dep)
{
for (auto [d, id] : val[x])
{
if (d >= dep)
{
grp[id].push_back({pre[min(cur, d - dep)], 0});
grp[suf[min(cur, d - dep)]].push_back({id, 0});
}
}
for (auto v : g[x])
{
if (v == fa || vis[v])
continue;
dfs4(v, x, dep + 1);
}
}
void calc(int x)
{
cnt = rt = 0;
mx[rt] = INF;
dfs(x, 0);
dfs2(x, 0);
vis[rt] = true;
cur = -1;
dfs3(rt, 0, 0);
dfs4(rt, 0, 0);
for (auto v : g[rt])
{
if (!vis[v])
calc(v);
}
}
int dis[N];
void dijkstra(int s)
{
mms(dis, 0x3f);
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({0, s});
dis[s] = 0;
while (!q.empty())
{
auto [ds, u] = q.top();
q.pop();
if (ds > dis[u])
continue;
for (auto [v, w] : grp[u])
{
if (dis[u] + w < dis[v])
{
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}
vector<long long> find_spread(signed N, signed M, vector<signed> A, vector<signed> B, vector<signed> P, vector<signed> D, vector<signed> C)
{
n = N;
m = M;
mx[0] = INF;
tot = n + m;
rep(i, 0, n - 2)
{
g[A[i] + 1].push_back(B[i] + 1);
g[B[i] + 1].push_back(A[i] + 1);
}
rep(i, 0, m - 1)
{
val[P[i] + 1].push_back({D[i], i + 1});
}
rep(i, 1, n) cst[i] = C[i - 1];
calc(1);
dijkstra(1);
rep(i, 1, m) if (dis[i] >= INF) dis[i] = -1;
return vector<long long>(dis + 1, dis + m + 1);
}
20241101
美麗的序列
暴力 dp 即可
Code
void add(int &x, int y)
{
x += y;
if (x >= MOD)
x -= MOD;
}
int n, m;
int mp[9][8][7][6][5][4][3][2], tmp[9][8][7][6][5][4][3][2];
int a[15];
void solve()
{
cin >> n >> m;
rep(i, 1, m) cin >> a[i];
sort(a + 1, a + m + 1);
reverse(a + 1, a + m + 1);
mp[a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]] = 1;
rep(i, 1, n)
{
mms(tmp, 0);
rep(a1, 0, a[1])
{
rep(a2, 0, a[2])
{
rep(a3, 0, a[3])
{
rep(a4, 0, a[4])
{
rep(a5, 0, a[5])
{
rep(a6, 0, a[6])
{
rep(a7, 0, a[7])
{
rep(a8, 0, a[8])
{
int aa1 = a1 + (a1 < a[1]), aa2 = a2 + (a2 < a[2]), aa3 = a3 + (a3 < a[3]), aa4 = a4 + (a4 < a[4]), aa5 = a5 + (a5 < a[5]), aa6 = a6 + (a6 < a[6]), aa7 = a7 + (a7 < a[7]), aa8 = a8 + (a8 < a[8]);
if (a[1] && aa1 == a[1])
{
aa1 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa1 = a1 + (a1 < a[1]);
}
if (a[2] && aa2 == a[2])
{
aa2 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa2 = a2 + (a2 < a[2]);
}
if (a[3] && aa3 == a[3])
{
aa3 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa3 = a3 + (a3 < a[3]);
}
if (a[4] && aa4 == a[4])
{
aa4 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa4 = a4 + (a4 < a[4]);
}
if (a[5] && aa5 == a[5])
{
aa5 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa5 = a5 + (a5 < a[5]);
}
if (a[6] && aa6 == a[6])
{
aa6 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa6 = a6 + (a6 < a[6]);
}
if (a[7] && aa7 == a[7])
{
aa7 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa7 = a7 + (a7 < a[7]);
}
if (a[8] && aa8 == a[8])
{
aa8 = 0;
add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]);
aa8 = a8 + (a8 < a[8]);
}
}
}
}
}
}
}
}
}
memcpy(mp, tmp, sizeof(mp));
}
int res = 0;
rep(a1, 0, a[1])
{
rep(a2, 0, a[2])
{
rep(a3, 0, a[3])
{
rep(a4, 0, a[4])
{
rep(a5, 0, a[5])
{
rep(a6, 0, a[6])
{
rep(a7, 0, a[7])
{
rep(a8, 0, a[8])
{
add(res, mp[a1][a2][a3][a4][a5][a6][a7][a8]);
}
}
}
}
}
}
}
}
cout << res << endl;
}
一
瞎搞做法,注意到如果不只有 add,sum 操作,序列實際要修改的數的個數是很少的,可以直接暴力
正確的做法是,用線段樹維護,add,sum是容易的,or,xor,and 都可以等價與子樹,合併反轉操作,打tag記錄即可
朋友
注意到每個魚最後停在一個區間,令 \(s_{i,j,k}\) 表示左右端點在 \([i,j]\) 內,包含 \(k\) 這個點的區間個數
考慮 \(dp_{l,r}\) 表示區間 \([l,r]\) 的答案,列舉這個區間中最大值的位置 \(k\)
Code
int n, m, w;
int a[N];
int dp[405][405];
pii rg[405];
int s[405][405][405];
vector<int> uni;
void solve()
{
cin >> n >> m >> w;
rep(i, 1, n) cin >> a[i];
rep(i, 1, m)
{
int pos, val;
cin >> pos >> val;
int l = pos, r = pos;
while (l > 1 && w - a[l] < val)
l--;
while (r < n && w - a[r] < val)
r++;
rg[i] = {l, r};
uni.push_back(l);
uni.push_back(r);
}
sort(ALL(uni));
uni.erase(unique(ALL(uni)), uni.end());
int cnt = sz(uni);
rep(i, 1, m)
{
rg[i].first = lower_bound(ALL(uni), rg[i].first) - uni.begin() + 1;
rg[i].second = lower_bound(ALL(uni), rg[i].second) - uni.begin() + 1;
debug(rg[i]);
rep(j, rg[i].first, rg[i].second) s[rg[i].first][rg[i].second][j]++;
}
rep(k, 1, cnt)
{
per(i, cnt, 1)
{
rep(j, i + 1, cnt)
{
s[i][j][k] += s[i + 1][j][k] + s[i][j - 1][k] - s[i + 1][j - 1][k];
}
}
}
rep(len, 1, cnt)
{
rep(l, 1, cnt - len + 1)
{
int r = l + len - 1;
rep(x, l, r)
{
chkmx(dp[l][r], dp[l][x - 1] + dp[x + 1][r] + (s[l][r][x] * (s[l][r][x] - 1) / 2));
}
}
}
cout << dp[1][cnt] << endl;
}
20241105
沙漠點列
點雙縮點,並計算非環上的邊的個數,這樣的邊滿足 \(low_v>dfn_u\)
然後貪心的先刪除這樣的邊,再從大到小操作環
考場因為點雙寫成邊雙掛分,注意這2者容易混,且小資料下可能結果一樣
Code
int n, m, k;
int low[N], dfn[N], tot;
stack<int> st;
vector<int> g[N];
int cnt = 0;
vector<int> vec;
struct union_set
{
int fa[N];
void init(int n)
{
iota(fa + 1, fa + n + 1, 1);
}
int getfa(int x)
{
return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void merge(int x, int y)
{
int fx = getfa(x), fy = getfa(y);
if (fx != fy)
fa[fx] = fy;
}
int count()
{
int res = 0;
for (int i = 1; i <= n; i++)
if (getfa(i) == i)
res++;
return res;
}
} uni;
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++tot;
st.push(u);
for (auto v : g[u])
{
if (v == fa)
continue;
if (!dfn[v])
{
tarjan(v, u);
chkmi(low[u], low[v]);
if (low[v] > dfn[u])
cnt++;
if (low[v] >= dfn[u])
{
int tmp = 1;
while (st.top() != v)
{
st.pop();
tmp++;
}
st.pop();
tmp++;
vec.push_back(tmp);
}
}
else
{
low[u] = min(low[u], dfn[v]);
}
}
}
void solve()
{
cin >> n >> m >> k;
uni.init(n);
rep(i, 1, m)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
uni.merge(u, v);
}
rep(i, 1, n) if (!dfn[i]) tarjan(i, 0);
int cur = uni.count();
if (k <= cnt)
{
cout << cur + k << endl;
return;
}
cur += cnt;
k -= cnt;
sort(ALL(vec), greater<int>());
debug(cur, k, cnt, vec);
for (auto v : vec)
{
if (k <= 1 || v == 2)
break;
if (k >= v)
{
k -= v;
cur += v - 1;
}
else
{
k--;
cur += k;
k = 0;
}
}
cout << cur << endl;
}
集合劃分
若一個集合 \(S\) 被選,所有 \(T\) 滿足 \(T\) 為 \(S\) 子集, \(|T|=|S|-1\) 的集合最多選 \(1\) 個,且 \(S-T\) 不屬於其它 \(B\) 選擇的集合
考慮 \(dp\) 表示已經選擇哪些數
Code
int n, m, k;
int st[N];
bool vis[N];
int fr[N], id[N];
void solve()
{
cin >> n >> m >> k;
rep(i, 1, m)
{
int x;
cin >> x;
st[x] = x;
}
rep(i, 1, (1 << n) - 1)
{
rep(j, 0, n - 1)
{
if (i & (1 << j))
st[i] |= st[i ^ (1 << j)];
}
}
vis[0] = true;
rep(msk, 1, (1 << n) - 1)
{
int x = __builtin_popcount(msk);
rep(i, 0, n - 1)
{
if (msk & (1 << i))
{
if (vis[msk ^ (1 << i)] && ((k & (1 << (n - x))) || (!(st[((1 << n) - 1) ^ msk ^ (1 << i)] & (1 << i)))))
{
vis[msk] = true;
fr[msk] = i;
}
}
}
}
if (!vis[(1 << n) - 1])
{
cout << "-1" << endl;
return;
}
for (int i = (1 << n) - 1, j = 0; i; i = (i ^ (1 << fr[i])), j++)
id[j] = fr[i];
rep(i, 1, (1 << n) - 1)
{
per(j, n - 1, 0)
{
if (i & (1 << id[j]))
{
cout << ((k >> j) & 1);
break;
}
}
}
}
染色
轉化為選出最多的不交區間,使得首尾顏色相同,維護是容易的,倍增即可
Code
int n, q;
int st[N][20];
int la[N];
int l, r;
int mx = 0;
int x;
int res;
int t;
void solve()
{
io.read(n);
io.read(q);
rep(i, 1, n)
{
io.read(x);
chkmx(mx, la[x]);
st[i][0] = mx;
la[x] = i;
}
rep(i, 1, 19)
{
rep(j, (1 << i), n)
{
st[j][i] = st[st[j][i - 1]][i - 1];
}
}
while (q--)
{
io.read(l);
io.read(r);
if (l > r)
swap(l, r);
res = 0;
t = (r - l + 1) * 2 - 2;
per(i, __lg(r - l + 1), 0)
{
if (st[r][i] >= l)
{
r = st[r][i];
res |= (1 << i);
}
}
io.write(t - res, '\n');
}
}
20241108
C 山遙路遠
考慮另一種判合法括號串的方式,即每次向左右擴充,直接spfa即可
遇到括號串,不只考慮字首和>=0,還有左右交替擴充
Code
int n, m, cq;
vector<pair<int, int>> g[N], rev[N];
int dp[N][N][2];
bool vis[N][N][2];
void solve()
{
cin >> n >> m >> cq;
rep(i, 1, m)
{
int u, v, w, c;
cin >> u >> v >> w >> c;
if (c == 2)
g[u].push_back({v, w});
else
rev[v].push_back({u, w});
}
priority_queue<pair<int, tuple<int, int, int>>, vector<pair<int, tuple<int, int, int>>>, greater<pair<int, tuple<int, int, int>>>> q;
mms(dp, 0x3f);
rep(i, 1, n)
{
dp[i][i][0] = 0;
q.push({0, {i, i, 0}});
}
while (!q.empty())
{
auto [ds, tmp] = q.top();
auto [u, v, ty] = tmp;
q.pop();
// vis[u][v][ty] = false;
// debug(u, v, ty);
if (dp[u][v][ty] != ds)
continue;
if (!ty)
{
rep(i, 1, n)
{
if (i != u && i != v && dp[u][v][0] + dp[v][i][0] < dp[u][i][0])
{
dp[u][i][0] = dp[u][v][0] + dp[v][i][0];
q.push({dp[u][i][0], {u, i, 0}});
}
if (i != u && i != v && dp[i][u][0] + dp[u][v][0] < dp[i][v][0])
{
dp[i][v][0] = dp[i][u][0] + dp[u][v][0];
q.push({dp[i][v][0], {i, v, 0}});
}
}
for (auto [t, w] : rev[u])
{
if (dp[t][v][1] > dp[u][v][ty] + w)
{
dp[t][v][1] = dp[u][v][ty] + w;
q.push({dp[t][v][1], {t, v, 1}});
}
}
}
else
{
for (auto [t, w] : g[v])
{
if (dp[u][t][0] > dp[u][v][ty] + w)
{
dp[u][t][0] = dp[u][v][ty] + w;
q.push({dp[u][t][0], {u, t, 0}});
}
}
}
}
while (cq--)
{
int u, v;
cin >> u >> v;
if (dp[u][v][0] >= INF)
cout << "-1\n";
else
cout << dp[u][v][0] % MOD << "\n";
}
}
D 命運歧途
\(n\leq 2000\)
對於每種 \(\pmod k\) 的值,可以分開進行容斥
20241109
A字首字尾
https://www.luogu.com.cn/problem/P9180
\(dp_i\) 表示字首到 \(i\) 時字尾的最大值,轉移貪心即可
Code
int n, q;
int a[N];
int mi[N][N];
int mx[N];
int la[N];
int tmp[N];
void solve()
{
cin >> n >> q;
rep(i, 1, n)
{
cin >> a[i];
}
rep(i, 1, n)
{
mx[i] = n;
mi[i][i] = a[i];
rep(j, i + 1, n)
{
mi[i][j] = min(mi[i][j - 1], a[j]);
}
}
int cnt = 0;
while (q--)
{
int len, lim;
cin >> len >> lim;
vector<pii> pre, suf;
rep(i, 1, n) tmp[i] = 0;
rep(i, 1, n - len + 1)
{
if (mi[i][i + len - 1] >= lim)
{
pre.push_back({i, i + len - 1});
suf.push_back({-(i + len - 1), -i});
}
}
reverse(ALL(suf));
// debug(pre);
rep(i, 1, n)
{
if (mx[i] <= 0)
continue;
auto it = lower_bound(ALL(suf), (pii){-mx[i], -INF});
if (it != suf.end())
chkmx(tmp[i], (-(*it).second) - 1);
it = lower_bound(ALL(pre), (pii){i, 0});
if (it != pre.end())
chkmx(tmp[(*it).second + 1], mx[i]);
}
bool flag = false;
bool flg = false;
rep(i, 1, n)
{
la[i] = mx[i];
mx[i] = max(tmp[i], mx[i - 1]);
if (mx[i] < i)
mx[i] = 0;
flag |= mx[i];
}
if (!flag)
{
rep(i, 1, n)
{
if (la[i] - i + 1 == len && mi[i][i + len - 1] >= lim)
{
cnt++;
break;
}
}
cout << cnt << endl;
return;
}
cnt++;
}
cout << cnt << endl;
}