<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      題解:Luogu P11516 [CCO 2024] Summer Driving

      題意

      給定一棵 \(n\) 個點的樹,有一個棋子初始時在 \(r\) 節點上。Alice 和 Bob 會輪流對棋子進行操作,Alice 先手。

      • Alice 操作時,必須把棋子沿著 \(A\) 條兩人都未走過的邊移動。
      • Bob 操作時,必須把棋子沿著至多 \(B\) 條邊移動。
      • 當 Alice 無法操作時,Bob 必須把棋子沿著至多 \(B\) 條兩人都未走過的邊移動。游戲結束。

      Alice 希望最大化最終棋子所在的節點編號,Bob 則希望最小化最終棋子所在的節點編號。求當兩人都以最佳策略行動時,最終棋子所在的節點編號。\(1\leq n\leq 3\times 10^5\)

      題解

      非常牛的題啊,被放到了 NOIP 模擬賽 T4。

      先判掉 \(A\leq B\) 的情況。此時每輪操作 Bob 都可以往節點 \(1\)\(B-A\) 步,因此答案顯然可以取到 \(1\)

      接下來我們有 \(A>B\)

      很容易想到的一步是,我們二分答案 \(mid\),把樹上編號 \(\geq mid\) 的點設為 \(1\)\(<mid\) 的點設為 \(0\),那么 check 就是要判斷 Alice 是否能走到 \(1\) 點。

      \(r\) 視作整棵樹的根,當 Alice 從點 \(x\) 開始操作時,不難發現 \(r\)\(x\) 的路徑上的邊都已經被走過了,因此 Alice 只會走向 \(x\) 子樹內的點。而由于 \(B<A\),Bob 操作完后也不會走出 \(sub_x\)。據此考慮樹形 DP。令 \(f_u\) 表示 Alice 從點 \(u\) 開始操作,會走到什么點,\(g_u\) 表示 Bob 從點 \(u\) 開始操作,會走到什么點。

      考慮 \(f\) 的轉移,如果 \(u\) 存在 \(A\) 級兒子,容易列出轉移方程:

      \[f_u=\bigvee_{\substack{v\in sub_u\\ \operatorname{dist}(u,v)=A}}g_v \]

      我們可以預處理出 \(u\) 的所有 \(A\) 級兒子進行轉移。具體來說,我們在 DFS 預處理的過程中存儲 DFS 棧,那么 \(stk_{top-A}\) 就是 \(stk_{top}\)\(A\) 級祖先。由于每個 \(v\) 恰好會被其 \(A\) 級祖先轉移一次,這部分復雜度是 \(\mathcal{O}(n)\) 的。

      但是 \(u\) 也可能不存在 \(A\) 級兒子,對應著此時 Alice 無法操作,游戲進入最終階段。那么我們就需要知道 \(sub_u\) 內是否有與 \(u\) 距離 \(\leq B\) 的點 \(v\),使得 \(v<mid\)。這是簡單的,我們樹形 DP 出 \(mn_u\) 表示 \(sub_u\) 內滿足 \(v<mid\) 的點 \(v\) 的最淺深度,判斷是否有 \(mn_u-dep_u>B\) 即可。\(mn\) 的轉移是顯然的:

      \[\begin{align*} mn_u&\leftarrow \begin{cases} dep_u, & u<mid\\ +\infty, & u\geq mid \end{cases}\\ mn_u&\leftarrow \min(mn_u,\min_{v\in son_u} mn_v) \end{align*} \]

      這部分同樣是 \(\mathcal{O}(n)\) 的。

      考慮 \(g\) 的轉移,第一眼的想法會是取 \(u\)\(B\) 鄰域內所有 \(f\) 值的與。但這是錯誤的!因為若 Bob 在 \(u\) 操作完后來到了 \(v\) 節點處,使得 \(v\)\(u\) 的祖先,那么 Alice 在 \(v\) 節點處操作時就不能走 \(u\) 節點對應的 \(v\) 的兒子所在的子樹了。

      這啟發我們再設計一個 DP 數組 \(h\),令 \(h_u\) 表示 Alice 從 \(fa_u\) 開始操作,不能走向 \(sub_u\),會走到哪個點。

      這樣我們就可以轉移 \(g\) 了。轉移分為兩部分:一部分是 \(u\)\(B\) 鄰域內,不為 \(u\) 的祖先的節點的 \(f\) 值的與,一部分是 \(u\)\(u\)\(B-1\) 級祖先的路徑上 \(h\) 值的與。

      但是這樣轉移 \(g\) 時會不會有后效性呢?如果我們直接 DFS 樹形 DP,是會有的。所以對于這題,我們要按節點的深度從大到小的順序 DP,然后\(g_u\) 掛在 \(u\)\(A\) 級祖先處更新,這樣由于 \(A>B\),我們在更新 \(g_u\) 時,其所需要用來轉移的信息都已經處理好了。

      再來考慮 \(h\) 的轉移,其實和 \(f\) 是類似的。我們\(h_u\) 掛在 \(fa_u\) 處更新。轉移時同樣要考慮此時 Alice 能否操作:

      • Alice 能操作:我們遍歷 \(fa_u\)\(A\) 級兒子,對它們的 \(f\) 值求和得到 \(sum\)。這樣扣除 \(sub_u\) 的貢獻時,只需用 \(sum\) 減去 \(sub_u\) 內的 \(f\) 值的和,然后判斷差是否 \(>0\)。這里需要預處理每個點 \(u\) 位于其 \(A\) 級祖先的哪個子樹中,同樣可以通過預處理時的 DFS 棧得到。
      • Alice 不能操作:和 \(f\) 類似,我們需要求出 \(sub_{fa_u}\) 內扣除 \(sub_u\) 后深度最淺的滿足 \(g_v=0\) 的點 \(v\),轉移 \(mn\) 時順便記錄非嚴格次小值即可。

      需要注意 Alice 何時不能操作:除了 \(fa_u\) 不存在 \(A\) 級兒子的情況外,還有一種情況 \(fa_u\)\(A\) 級兒子全部位于 \(sub_u\) 內。這也是好判的。

      至此,我們得到了 \(\mathcal{O}(n^2\log{n})\) 的解法。

      考慮優化,顯然瓶頸在于 \(g\) 的轉移。可以把 \(g_u\) 的轉移拆成兩部分:

      • 對不是 \(u\) 祖先的點 \(v\),求離 \(u\) 最近的使得 \(f_v=0\) 的點 \(v\) 的距離。
      • \(u\)\(u\)\(B-1\) 級祖先的路徑上是否存在點 \(v\) 使得 \(h_v=0\)

      直接做看起來沒前途。我們不妨轉換思路,考察每個點 \(v\)\(g_u\) 的影響。

      當處理深度為 \(d\) 的點時,我們會更新深度為 \(d+A\) 處的 \(g\) 值,此時 \(g\) 用到的點的深度必然 \(\geq d+A-B\)。于是我們在開始處理第一個深度為 \(d\) 的點時,加入所有深度為 \(d+A-B\) 的點來更新它們的影響。

      對于第一種轉移,我們注意到 \(\operatorname{lca}(u,v)\) 必然處于 \(u\)\(u\)\(B-1\) 級祖先的路徑上,因此考慮在 \(\operatorname{lca}\) 處統計貢獻。更具體地,當加入一個節點 \(x\) 時,我們維護出 \(mnf_x\) 表示 \(sub_x\) 內滿足 \(g_v=0\) 的點 \(v\) 的最淺深度,然后嘗試用它來更新 \(x\)兄弟節點的對應子樹,也就是此時我們以 \(fa_x\) 作為 \(\operatorname{lca}\) 統計貢獻。我們在 DFS 序上建一顆線段樹,把這些兄弟節點對應的 DFS 序區間對 \(mnf_x-2dep_{fa_x}\)\(\min\) 即可。這樣子在求離點 \(u\) 最近的 \(f_v=0\) 的點的距離時,只需在將段樹上單點查詢得到的值加上 \(dep_u\) 即可得到真實值。注意 \(x\) 的兄弟節點對應的 DFS 序區間可以被拆分成至多 \(2\) 個連續區間。

      第二種轉移其實是簡單的。當加入一個節點 \(x\) 時,我們遍歷其所有兒子 \(y\),若 \(h_y=0\),我們就把 \(y\) 對應的 DFS 序區間對 \(-\infty\)\(\min\),使得子樹內的 \(g\) 全部置為 \(0\)。注意這里會有多余的、深度更大的位置的 \(g\) 值被置為 \(0\),但是由于這些位置的 \(g\) 值我們前面已經計算出來了,所以實際上沒有影響。

      于是,轉移 \(g_u\) 時,我們只需將線段樹上單點查詢得到的值加上 \(dep_u\) 后和 \(mnf_u-dep_u\)\(\min\),判斷其是否 \(>B\) 即可。

      線段樹需要支持區間 \(\operatorname{chkmin}\) 和單點查詢,使用標記永久化即可。

      于是我們成功把 check 部分的復雜度優化到了 \(\mathcal{O}(n\log{n})\)。總體時間復雜度為 \(\mathcal{O}(n\log^2{n})\)

      代碼

      仔細想清楚你要做什么,代碼就不會很難寫了。

      這里放出代碼的主要部分,未經刻意卡常即可通過本題。

      struct SegTree {
      #define ls(p) (p << 1)
      #define rs(p) (p << 1 | 1)
      	int tg[N << 2];
      	inline void clr() { fill(tg + 1, tg + (n << 2) + 1, INF); }
      	inline void upd(int p, int l, int r, int x, int y, int v) {
      		if (x <= l && y >= r) return chk_min(tg[p], v), void();
      		int mid = l + r >> 1;
      		if (x <= mid) upd(ls(p), l, mid, x, y, v);
      		if (y > mid) upd(rs(p), mid + 1, r, x, y, v);
      	}
      	inline int query(int p, int l, int r, int x) {
      		if (l == r) return tg[p];
      		int mid = l + r >> 1;
      		return min(tg[p], x <= mid ? query(ls(p), l, mid, x) : query(rs(p), mid + 1, r, x));
      	}
      #undef ls
      #undef rs
      } sgt;
      
      inline void dfs_pre(int x) {
      	stk[dep[x] = ++top] = x, ldfn[x] = ++stmp;
      	if (top > A) son[stk[top - A]].push_back(x), ++cnts[bel[x] = stk[top - A + 1]];
      	for (int i = t.head[x]; ~i; i = t.nxt[i]) {
      		int y = t.to[i];
      		if (y == fa[x]) continue;
      		fa[y] = x, dfs_pre(y);
      	}
      	--top, rdfn[x] = stmp;
      }
      inline bool check(int mid) {
      	fill(sum + 1, sum + n + 1, 0), sgt.clr();
      	for (int i = 1, k = 1; i <= n; ++i) {
      		int x = p[i];
      		if (dep[x] ^ dep[p[i - 1]]) {
      			int dd = dep[x] + A - B;
      			while (k <= n && dep[p[k]] == dd) {
      				int y = p[k], fy = fa[y];
      				for (int j = t.head[y]; ~j; j = t.nxt[j]) {
      					int s = t.to[j];
      					if (s == fy) continue;
      					if (!h[s]) sgt.upd(1, 1, n, ldfn[s], rdfn[s], -INF);
      				}
      				int val = mnf[y] - dep[fy] * 2;
      				int l = ldfn[fy] + 1, r = rdfn[fy];
      				if (l < ldfn[y]) sgt.upd(1, 1, n, l, ldfn[y] - 1, val);
      				if (rdfn[y] < r) sgt.upd(1, 1, n, rdfn[y] + 1, r, val);
      				++k;
      			}
      		}
      		mn0[x] = x < mid ? dep[x] : INF;
      		int smn = INF;
      		for (int j = t.head[x]; ~j; j = t.nxt[j]) {
      			int y = t.to[j];
      			if (y == fa[x]) continue;
      			if (mn0[y] < mn0[x]) smn = mn0[x], mn0[x] = mn0[y];
      			else chk_min(smn, mn0[y]);
      		}
      		if (son[x].empty()) {
      			f[x] = mn0[x] - dep[x] > B, mnf[x] = !f[x] ? dep[x] : INF;
      			for (int j = t.head[x]; ~j; j = t.nxt[j]) {
      				int y = t.to[j];
      				if (y == fa[x]) continue;
      				int val = mn0[y] == mn0[x] ? smn : mn0[x];
      				h[y] = val - dep[x] > B;
      				chk_min(mnf[x], mnf[y]);
      			}
      		} else {
      			int cnt = 0;
      			for (int y : son[x]) {
      				g[y] = min(mnf[y] - dep[y], sgt.query(1, 1, n, ldfn[y]) + dep[y]) > B;
      				cnt += g[y], sum[bel[y]] += g[y];
      			}
      			f[x] = cnt, mnf[x] = !f[x] ? dep[x] : INF;
      			for (int j = t.head[x]; ~j; j = t.nxt[j]) {
      				int y = t.to[j];
      				if (y == fa[x]) continue;
      				chk_min(mnf[x], mnf[y]);
      				if (cnts[y] < son[x].size()) h[y] = cnt - sum[y];
      				else {
      					int val = mn0[y] == mn0[x] ? smn : mn0[x];
      					h[y] = val - dep[x] > B;
      				}
      			}
      		}
      	}
      	return f[rt];
      }
      
      posted @ 2025-08-23 23:04  P2441M  閱讀(7)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 中文字幕人妻无码一夲道| 好姑娘6电影在线观看| 阿拉善右旗| 亚洲综合精品一区二区三区| 欧美乱码伦视频免费| 久久国产欧美日韩精品图片| 偷拍美女厕所尿尿嘘嘘小便| 激情视频乱一区二区三区| 亚洲国产午夜精品福利| 亚洲人成人网站色www| 成人看的污污超级黄网站免费| 亚洲AV永久中文无码精品综合| 伊人精品无码av一区二区三区 | 黑人巨大av无码专区| 中文字幕人妻互换av久久| 成人国产精品免费网站| 国产一区二区三区不卡视频| 少妇伦子伦精品无吗| 热99久久这里只有精品| 漠河县| 变态另类视频一区二区三区| 开原市| 国产精品免费视频不卡| 一区二区丝袜美腿视频| 精品一区二区不卡无码AV| 99久久国产一区二区三区| 人人爽亚洲aⅴ人人爽av人人片| 内射极品少妇xxxxxhd| 成人网站免费观看| 日韩精品成人区中文字幕| 郁南县| 国产精品夫妇激情啪发布| 日韩人妻一区中文字幕| 国产亚洲AV电影院之毛片| 曰韩无码二三区中文字幕| 四虎成人免费视频在线播放| 国产激情第一区二区三区 | 国产精品久久久久影院亚瑟| 亚洲另类无码一区二区三区| 91人妻熟妇在线视频| 国产精品久久久久久无毒不卡|