日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[选拔赛2 NOIP2018雅礼集训 Day3 u,v,w]玩个三角形(二维差分),玩个球(状压DP+map),玩个树(树上DP)

發布時間:2023/12/3 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [选拔赛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去存

這道題仔細一看,就是個披著期望皮的記憶化搜索羊

為什么呢?很簡單,你想嘛~
當后面剩下的顏色狀態一樣時,操作的概率與期望計算方式與之前肯定是一樣的,答案也肯定一樣
那我們為什么要傻逼兮兮的懟上去再來一遍??


接下來我將重點講解代碼中賊冗長的一行代碼的意思,因為實在是太妙了!!

int sta1 = sta >> 1 & ~ ( ( 1 << i - 1 ) - 1 ) | sta & ( ( 1 << i - 1 ) - 1 );

如果不熟悉位運算的優先級的,看到這里基本上已經告退了吧哈哈哈哈
接下來讓我們加幾個括號

int sta1 = ( ( sta >> 1 ) & ( ~ ( ( 1 << ( i - 1 ) ) - 1 ) ) ) | ( sta & ( ( 1 << ( i - 1 ) ) - 1 ) );

我們來一一分析,提前聲明因為我的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)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。