[选拔赛2 NOIP2018雅礼集训 Day3 u,v,w]玩个三角形(二维差分),玩个球(状压DP+map),玩个树(树上DP)
文章目錄
- T1:玩個三角形
- title
- solution
- code
- T2:玩個球
- title
- solution
- code
- T3:玩個樹
- title
- solution
- code
T1:玩個三角形
title
題目描述
考慮一個 n ? n 的矩陣 A,初始所有元素均為 0。
執行 q 次如下形式的操作:給定 4 個整數 r, c, l, s,對于每個滿足 x ∈ [r, r+l), y ∈ [c, x-r+c] 的元素 (x, y),將權值增加 s。也就是,給一個左上頂點為 (r, c)、直角邊長為 l 的下三角區域加上 s。
輸出最終矩陣的元素異或和。
輸入格式
第一行兩個整數 n, q。
接下來 q 行,每行四個整數 r, c, l, s,代表一次操作。
輸出格式
輸出一行,一個整數,代表答案。
樣例
樣例輸入
10 4
1 1 10 1
5 5 4 4
1 9 4 3
3 3 5 2
樣例輸出
0
樣例解釋
1 0 0 0 0 0 0 0 3 0
1 1 0 0 0 0 0 0 3 3
1 1 3 0 0 0 0 0 3 3
1 1 3 3 0 0 0 0 3 3
1 1 3 3 7 0 0 0 0 0
1 1 3 3 7 7 0 0 0 0
1 1 3 3 7 7 7 0 0 0
1 1 1 1 5 5 5 5 0 0
1 1 1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1 1 1
數據范圍與提示
對于100%的數據,保證 n ∈ [1, 103],q ∈ [0, 3 ? 105],r, c, l ∈ [1, n],s ∈ [1, 109]。
solution
考慮用二維樹組差分維護,如果是個矩形就是特別簡單的了,可惜是個直角三角形
那么就拆一下,用兩個二維數組,一個專門維護單列,一個專門維護斜邊
code
#include <cstdio> #define MAXN 2005 #define ll long long int n, Q; ll ans; ll col[MAXN][MAXN], slope[MAXN][MAXN], matrix[MAXN][MAXN];int main() {scanf( "%d %d", &n, &Q );for( int i = 1, r, c, l, s;i <= Q;i ++ ) {scanf( "%d %d %d %d", &r, &c, &l, &s );col[r][c] += s, col[r + l][c] -= s;slope[r][c + 1] += s, slope[r + l][c + l + 1] -= s;}for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )col[i][j] += col[i - 1][j];for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )slope[i][j] += slope[i - 1][j - 1];for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )matrix[i][j] += matrix[i][j - 1] + col[i][j] - slope[i][j];for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )ans ^= matrix[i][j];printf( "%lld", ans );return 0; }T2:玩個球
title
有 n 個球排成一行,每個球的顏色為黑或白。
執行 k 次操作,第 i(1 ≤ i ≤ k) 次操作形式如下:
? 從 [1, n-i+1] 中,等概率隨機選擇一個整數 x。
? 移除從左往右數的第 x 個球,或從右往左數的第 x 個球(也就是從左往右數的第 n-i+2-x 個)。之后,所有右側的球的編號減 1。
給定每個球的顏色信息,希望最大化移除的白球數量。
輸出在最優策略下,期望的移除白球數量。誤差在1e-6范圍內,即算正確
輸入格式
第一行,兩個整數 n, k。 第二行,一個長度為 n、僅由 ‘W’ 和 ‘B’ 組成的字符串,第 i 個字符代表第 i 個球的顏色, ‘W’ 為白色, ‘B’ 為黑色。
輸出格式
輸出一行,一個浮點數,代表答案。
樣例
樣例輸入1
3 1
BWW
樣例輸出1
1.0000000000
樣例解釋1
如果 x = 1,從右側操作,如果 x = 2 或 3,從左側操作,均可以移除一個白球。
樣例輸入2
4 2
WBWB
樣例輸出2
1.5000000000
數據范圍與提示
對于100%的數據,保證 1 ≤ n ≤ 30,0 ≤ k ≤ n
solution
nnn怎么這么小?!!準定要搞事!!
老子反手就是一個狀壓,壓不死你
但是仔細一想,2302^{30}230好像逼近intintint極限值
不要擔心,一般這種大數據數組存不下,就找藍翔技術學校STLSTLSTL,這些容器關鍵時刻還是不會掉鏈子的,這里可以采取mapmapmap與數組進行分工合作,數組存不下的就用mapmapmap去存
這道題仔細一看,就是個披著期望皮的記憶化搜索羊
為什么呢?很簡單,你想嘛~
當后面剩下的顏色狀態一樣時,操作的概率與期望計算方式與之前肯定是一樣的,答案也肯定一樣
那我們為什么要傻逼兮兮的懟上去再來一遍??
接下來我將重點講解代碼中賊冗長的一行代碼的意思,因為實在是太妙了!!
如果不熟悉位運算的優先級的,看到這里基本上已經告退了吧哈哈哈哈
接下來讓我們加幾個括號
我們來一一分析,提前聲明因為我的iii是從111開始枚舉的,而二進制是從000開始,故要?1-1?1
如何才能把iii這一個球徹底刪掉呢??讓我們拭目以待吧!!
( 1 << ( i - 1 ) ) - 1
從[0,i)[0,i)[0,i)位每一位都為111然后其余位全為000,好理解,下一個!
sta >> 1
全體右移一位,第000位丟掉,好理解,下一個!
~ ( ( 1 << ( i - 1 ) ) - 1 ) )
~ : 一種單目運算符(位運算),對二進制每一位上的數全都進行取反操作,1變0,0變1
接下來進行結合操作!!吃俺老孫一棒
( ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) )這行語句的目的是保留了[i+1,tot][i+1,tot][i+1,tot]位上的每一個球的狀態,即畫圈部分
注意sta>>1sta>>1sta>>1整體右移111位后再取&\&&,而我們知道除了[0,i)[0,i)[0,i)其余位置都為111
故而stastasta右移111位后,以iii為分水嶺,原來的狀態是什么,右移111位的狀態亦不變
而iii恰好右移成為i?1i-1i?1,從i?1i-1i?1位開始往右全都是000
剛好把iii給卡掉了!
最后就是上面一堆的狀態[0,i)[0,i)[0,i)還是均為000的,此時就需要得到原來這些位置上的狀態
sta & ( ( 1 << ( i - 1 ) ) - 1 )最后兩個殘缺狀態取∣|∣,即是卡掉iii后的新狀態
這是其中一種,另一種同理,不再贅述
code
#include <iostream> #include <cstdio> #include <map> using namespace std; map < int, double > mp; int n, k; char s[40]; double dp[1 << 25];double dfs( int sta, int tot ) {if( tot == n - k ) return 0;if( tot > 24 && mp.find( sta ) != mp.end() ) return mp[sta];if( tot <= 24 && dp[sta] != -1 ) return dp[sta];double sum = 0;for( int i = 1;i <= ( tot >> 1 );i ++ ) {int color1 = ( sta >> ( i - 1 ) ) & 1;int color2 = ( sta >> ( tot - i ) ) & 1;int sta1 = ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) | ( sta & ( ( 1 << ( i - 1 ) ) - 1 ) );int sta2 = ( sta >> 1 ) & ( ~ ( ( 1 << ( tot - i ) ) - 1 ) ) | ( sta & ( ( 1 << ( tot - i ) ) - 1 ) );sum += 2 * max( dfs( sta1, tot - 1 ) + color1, dfs( sta2, tot - 1 ) + color2 ) / tot;}if( tot & 1 ) {int i = ( tot + 1 ) >> 1; int color = ( sta >> ( i - 1 ) ) & 1;int st = ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) | ( sta & ( ( 1 << ( i - 1 ) ) - 1 ) );sum += ( dfs( st, tot - 1 ) + color ) / tot;}return ( tot > 24 ) ? mp[sta] = sum : dp[sta] = sum; }int main() {scanf( "%d %d %s", &n, &k, s );int sta = 0;for( int i = 0;i < ( 1 << 25 );i ++ )dp[i] = -1;for( int i = 1;i <= n;i ++ )sta |= ( s[i - 1] == 'W' ) << ( n - i );sta |= ( 1 << n );printf( "%.8f", dfs( sta, n ) );return 0; }T3:玩個樹
title
有一棵 n 個節點的樹,每條邊長度為 1,顏色為黑或白。
可以執行若干次如下操作:選擇一條簡單路徑,反轉路徑上所有邊的顏色。
對于某些邊,要求在操作結束時為某一種顏色。
給定每條邊的初始顏色,求最小操作數,以及滿足操作數最小時,最小的操作路徑長度和。
輸入格式
第一行,一個正整數 n。
接下來 n-1 行,每行四個整數 a, b, c, d:
? 樹中有一條邊連接 a 和 b。
? c = 0, 1 表示初始顏色為白色、黑色。
? d = 0, 1, 2 表示最終要求為白色、要求為黑色、沒有要求。
輸出格式
輸出一行,兩個整數,表示最小操作數、操作數最小時的最小路徑長度和。
樣例
樣例輸入1
5
2 1 1 0
1 3 0 1
2 4 1 2
5 2 1 1
樣例輸出1
1 2
樣例解釋1
操作路徑 {2, 1, 3}。
樣例輸入2
3
1 3 1 2
2 1 0 0
樣例輸出2
0 0
樣例輸入3
6
1 3 0 1
1 2 0 2
2 4 1 0
4 5 1 0
5 6 0 2
樣例輸出3
1 4
數據范圍與提示
對于100%的數據,保證給出的圖為一棵樹,有 n ∈ [1,1e5],a, b ∈ [1, n],c ∈ {0, 1},d ∈ {0, 1, 2}
solution
若操作的邊集為EEE,那么操作次數則為EEE中度數為奇數的點的個數/2/2/2,稱這種點為端點
對于點iii而言
若為奇數,除去與父親相連的邊則變成了偶數,兒子可以兩兩配對
那么它就孤了下來,成為一條有可能被操作的路徑的一端
若為偶數,除去與父親相連的邊則變成了奇數,兒子兩兩配對后則還剩一個兒子
那么此時iii稱當一個中轉站的作用,銜接著兒子與父親
反正無論如何它都只可能是一條可能被操作的路徑上的一個點,僅此而已
此時赤裸裸的思想,先控制操作數最少,其次控制路徑最小
設dp[i][0/1]dp[i][0/1]dp[i][0/1]表示以iii為根的子樹(iii與父親fafafa相連的那條邊是否翻轉,翻轉為111,不翻轉為000)的最小操作數以及最小路徑和
再設w1w1w1表示iii與父親fafafa相連的邊需要翻轉,w2w2w2表示iii與父親fafafa相連的邊不需要翻轉
w1=min(dp[v][0]+w1,w2+dp[v][1])w1=min(dp[v][0]+w1,w2+dp[v][1])w1=min(dp[v][0]+w1,w2+dp[v][1])
①兒子不需要翻轉,iii與父親的邊已經翻轉
②iii與父親的邊尚未翻轉,兒子需要翻轉
w2=min(w2+dp[v][0],w1+dp[v][1])w2=min(w2+dp[v][0],w1+dp[v][1])w2=min(w2+dp[v][0],w1+dp[v][1])
①兒子不翻轉,父親也不翻轉
②兒子翻轉,父親再翻轉回來
code
#include <cstdio> #include <vector> using namespace std; #define MAXN 100005 #define inf 0x3f3f3f3f struct node {int opt, len;node(){}node( int Opt, int Len ) {opt = Opt, len = Len;}node operator + ( const node &t ) const {return node( opt + t.opt, len + t.len );} }dp[MAXN][2]; vector < pair < int, int > > G[MAXN]; int n;node Min( node x, node y ) {if( x.opt < y.opt ) return x;else if( x.opt == y.opt && x.len < y.len ) return x;else return y; }void dfs( int u, int fa, int type ) {node w1 = node( inf, inf ), w2 = node( 0, 0 );for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first, flag = G[u][i].second;if( v == fa ) continue;dfs( v, u, flag );node tmp1 = Min( w1 + dp[v][0], w2 + dp[v][1] );node tmp2 = Min( w1 + dp[v][1], w2 + dp[v][0] );w1 = tmp1, w2 = tmp2;}if( type == 1 || type == 2 )dp[u][1] = Min( node( w1.opt, w1.len + 1 ), node( w2.opt + 1, w2.len + 1 ) );elsedp[u][1] = node( inf, inf );if( type == 0 || type == 2 )dp[u][0] = Min( node( w1.opt + 1, w1.len ), w2 );elsedp[u][0] = node( inf, inf ); }int main() {scanf( "%d", &n );for( int i = 1, a, b, c, d;i < n;i ++ ) {scanf( "%d %d %d %d", &a, &b, &c, &d );c = ( d == 2 ) ? d : c != d;G[a].push_back( make_pair( b, c ) );G[b].push_back( make_pair( a, c ) );}dfs( 1, 0, 2 );printf( "%d %d", dp[1][0].opt >> 1, dp[1][0].len );return 0; }總結
以上是生活随笔為你收集整理的[选拔赛2 NOIP2018雅礼集训 Day3 u,v,w]玩个三角形(二维差分),玩个球(状压DP+map),玩个树(树上DP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FTP命令大全
- 下一篇: 数论一之定理证明——裴蜀/威尔逊/费马/