CodeForces 1610H Squid Game(延迟贪心 + 构造 + 树状数组)
problem
洛谷鏈接
solution
考慮重新隨便欽定一個點為“根”,并且強制根必須是關鍵點。
則所有 x?yx-yx?y 不是直系祖先-子代的要求(要求Ⅰ),即 xxx 不是 yyy 祖先,yyy 也不是 xxx 祖先,一定都被滿足。
所有 x?yx-yx?y 是直系祖先-子代的要求(要求Ⅱ)都不能被滿足。根離這條路徑最近的點一定是 x/yx/yx/y 中某個端點。
下面不妨仍認為 111 為根。考慮怎么才能最少且滿足所有的要求Ⅱ。
采用延遲貪心的思想。從下往上考慮盡量晚放關鍵點(最淺化關鍵點的深度)。
感性想想這確實是最優的
到這里都是基于根被選為關鍵點的后續操作,但是這個根一定要成為關鍵點嗎?
也有可能存在,考慮要求Ⅱ的時候,晚放的某些關鍵點恰好把所有的要求Ⅰ也順帶解決了的情況。
所以延遲貪心后,又反過來考慮是否要新增一個根為關鍵點,增加 111 的貢獻。
換言之,在強制根的想法上找到了解法發現可行,現在回來再考慮假設的必要性。
發現,如果為了解決要求Ⅱ而放置的關鍵點無法解決要求Ⅰ,則要求Ⅰ的 lca\text{lca}lca 一定深度更小 / 這個關鍵點一定在要求Ⅰ的某個端點子樹內。
只要有一個要求Ⅰ無法被滿足,都意味著這個根作為關鍵點是必需的。
dfs 樹,利用 dfndfndfn 序,用樹狀數組維護關鍵點的信息即可。
注意:雖然之前說隨便找個點做根,但這只是一種思考方式,并不是真的在代碼中進行換根操作。
code
#include <bits/stdc++.h> using namespace std; #define maxn 300005 vector < pair < int, int > > MS; vector < int > G[maxn], E[maxn]; int n, m, cnt; int dep[maxn], L[maxn], R[maxn], t[maxn], id[maxn]; int f[maxn][20];void dfs( int u ) {for( int i = 1;i < 20;i ++ ) f[u][i] = f[f[u][i - 1]][i - 1];L[u] = ++ cnt;for( int v : G[u] ) {dep[v] = dep[u] + 1;dfs( v );}R[u] = cnt; }int lca( int x, int d ) { for( int i = 19;~ i;i -- ) if( d >> i & 1 ) x = f[x][i]; return x; }void Add( int i ) { for( ;i <= n;i += i & -i ) t[i] ++; }int Ask( int i ) { int ans = 0; for( ;i;i -= i & -i ) ans += t[i]; return ans; }int get( int x ) { return Ask( R[x] ) - Ask( L[x] - 1 ); }int main() {scanf( "%d %d", &n, &m );for( int i = 2;i <= n;i ++ ) {scanf( "%d", &f[i][0] );G[f[i][0]].push_back( i );}dfs( 1 );for( int i = 1, u, v;i <= m;i ++ ) {scanf( "%d %d", &u, &v );if( dep[u] > dep[v] ) swap( u, v );if( f[v][0] == u ) return ! printf( "-1\n" );E[u].push_back( v );}iota( id + 1, id + n + 1, 1 );sort( id + 1, id + n + 1, []( int x, int y ) { return dep[x] > dep[y]; } );int ans = 0;for( int i = 1;i <= n;i ++ ) {int x = id[i];for( int y : E[x] ) {if( dep[x] == dep[y] ) MS.push_back( { x, y } ); //兩個深度一樣則一定隸屬于不同子樹else {int z = lca( y, dep[y] - dep[x] - 1 ); //爬到深度比x深1的祖先點if( f[z][0] ^ x ) MS.push_back( { x, y } ); //證明x,y不屬于同一個子樹else {if( get( z ) - get( y ) ) continue; //祖先-子代關系 判斷x-y這條鏈(除了x,y)有沒有關鍵點存在else ans ++, Add( L[z] );//新增關鍵點}}}}for( int i = 0;i < MS.size();i ++ ) {int x = MS[i].first, y = MS[i].second;if( get( x ) + get( y ) == ans ) { ans ++; break; } //判斷x,y子樹包含了所有的關鍵點,那么這個x-y旁系祖先-子代關系就必須通過根來滿足了}printf( "%d\n", ans );return 0; }總結
以上是生活随笔為你收集整理的CodeForces 1610H Squid Game(延迟贪心 + 构造 + 树状数组)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: U盘量产工具
- 下一篇: CF1621G Weighted Inc