noip2024模擬賽記錄

xiaruize發表於2024-11-28

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

美麗的序列

image

暴力 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;
}

image

瞎搞做法,注意到如果不只有 add,sum 操作,序列實際要修改的數的個數是很少的,可以直接暴力

正確的做法是,用線段樹維護,add,sum是容易的,or,xor,and 都可以等價與子樹,合併反轉操作,打tag記錄即可

朋友

image

注意到每個魚最後停在一個區間,令 \(s_{i,j,k}\) 表示左右端點在 \([i,j]\) 內,包含 \(k\) 這個點的區間個數

考慮 \(dp_{l,r}\) 表示區間 \([l,r]\) 的答案,列舉這個區間中最大值的位置 \(k\)

\[dp_{l,r}=dp_{l,k-1}+dp_{k+1,r}+\binom{s_{l,r,k}}{2} \]

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

沙漠點列

image

點雙縮點,並計算非環上的邊的個數,這樣的邊滿足 \(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;
}

集合劃分

image

若一個集合 \(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;
            }
        }
    }
}

染色

image

轉化為選出最多的不交區間,使得首尾顏色相同,維護是容易的,倍增即可

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 山遙路遠

image

考慮另一種判合法括號串的方式,即每次向左右擴充,直接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 命運歧途

image

\(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;
}

拉起窗簾

相關文章