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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

[省选联考 2020 A/B 卷] 信号传递(状压dp + 卡空间)

發布時間:2023/12/3 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [省选联考 2020 A/B 卷] 信号传递(状压dp + 卡空间) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

problem

luogu-P6622

一條道路上從左至右排列著 mmm 個信號站,初始時從左至右依次編號為 1,2,…,m1,2,\dots,m1,2,,m,相鄰信號站之間相隔 111 單位長度。

每個信號站只能往它右側的任意信號站傳輸信號(稱為普通傳遞),每單位長度距離需要消耗 111 單位時間。

道路的最左側有一個控制塔,它在最左側信號站的左側,與其相隔 111 單位長度。

控制塔能與任意信號站進行雙向信號傳遞(稱為特殊傳遞),但每單位長度距離需要消耗 kkk 個單位時間。

對于給定的長度為 nnn 的信號傳遞序列 SSS,傳遞規則如下:

  • n?1n-1n?1 次信號傳遞,第 iii 次信號傳遞將把信號從 SiS_iSi? 號信號站傳遞給 Si+1S_{i+1}Si+1? 號。
  • Si+1S_{i+1}Si+1? 號信號站在 SiS_iSi? 號右側,則將使用普通傳遞方式,從 SiS_iSi? 號直接傳遞給 Si+1S_{i+1}Si+1? 號。
  • Si+1S_{i+1}Si+1?號信號站在 SiS_iSi? 號左側,則將使用特殊傳遞方式,信號將從 SiS_iSi? 號傳遞給控制塔,再由控制塔傳遞給 Si+1S_{i+1}Si+1? 號。
  • Si=Si+1S_i=S_{i+1}Si?=Si+1?,則信號無須傳遞。
  • 阿基作為大工程師,他能夠任意多次交換任意兩個信號站的位置,即他能夠重排信號站的順序,這樣會使得 SSS 消耗的傳遞時間改變。

    現在阿基想知道,在他重排信號站順序后,SSS 所消耗的傳遞時間最小能是多少。

    solution

    假設坐標在 x,yx,yx,y 的兩個信號塔之間有一次 x→yx\rightarrow yxy 的傳遞,可以形式化地表示:
    {y?xx≤ykx+kyx>y\begin{cases} y-x&&x\le y\\ kx+ky&&x>y \end{cases} {y?xkx+ky??xyx>y?
    這是同構的,也就是說對于每個傳遞的貢獻可以拆成兩個信號塔的各自貢獻。

    假設坐標在 yyy 的信號塔,如果有坐標 x→yx\rightarrow yxy 的傳遞。

    • 則對于 yyy 所代表的信號塔而言:
      • x≤yx\le yxy,產生 111
      • x>yx>yx>y,產生 kkk
    • 反之同理,對于 xxx 所代表的信號塔而言:
      • x≤yx\le yxy,產生 ?1-1?1
      • x>yx>yx>y,產生 kkk

    則最終代價等于每個點的貢獻乘以其坐標再求和。

    具體的數學形式推導:

    cnt(i,j):cnt(i,j):cnt(i,j): 有多少次信號塔 i→ji\rightarrow jij 的傳遞,即 cnt(i,j)=∑k=1n[Sk=i][Sk+1=j]cnt(i,j)=\sum_{k=1}^n[S_k=i][S_{k+1}=j]cnt(i,j)=k=1n?[Sk?=i][Sk+1?=j]

    設下標 ppp 處是 idpid_pidp? 號信號站,推導一下每個下標對答案的貢獻。
    ans=∑i=1m∑j=i+1mcnt(idi,idj)?(j?i)+∑i=1m∑j=1i?1cnt(idi,idj)?(i+j)?kans=\sum_{i=1}^m\sum_{j=i+1}^mcnt(id_i,id_j)*(j-i)+\sum_{i=1}^m\sum_{j=1}^{i-1}cnt(id_i,id_j)*(i+j)*k ans=i=1m?j=i+1m?cnt(idi?,idj?)?(j?i)+i=1m?j=1i?1?cnt(idi?,idj?)?(i+j)?k=∑i=1mi(∑j=i+1m(k?cnt(idj,idi)?cnt(idi,idj))+∑j=1i?1(k?cnt(idi,idj)+cnt(idj,idi)))=\sum_{i=1}^mi\bigg(\sum_{j=i+1}^m\Big(k*cnt(id_j,id_i)-cnt(id_i,id_j)\Big)+\sum_{j=1}^{i-1}\Big(k*cnt(id_i,id_j)+cnt(id_j,id_i)\Big)\bigg) =i=1m?i(j=i+1m?(k?cnt(idj?,idi?)?cnt(idi?,idj?))+j=1i?1?(k?cnt(idi?,idj?)+cnt(idj?,idi?)))

    f(s):f(s):f(s): 考慮到 ∣s∣|s|s 位(∣s∣:s|s|:ss:s 二進制下 111 的個數),前 ∣s∣|s|s 位的信號站編號集合為 sss 時的最小代價。

    那么 ∑j=i+1m(k?cnt(idj,idi)?cnt(idi,idj))+∑j=1i?1(k?cnt(idi,idj)+cnt(idj,idi))\sum_{j=i+1}^m\Big(k*cnt(id_j,id_i)-cnt(id_i,id_j)\Big)+\sum_{j=1}^{i-1}\Big(k*cnt(id_i,id_j)+cnt(id_j,id_i)\Big)j=i+1m?(k?cnt(idj?,idi?)?cnt(idi?,idj?))+j=1i?1?(k?cnt(idi?,idj?)+cnt(idj?,idi?)) 形式中的 jjj 其實就是以 ∣s∣|s|s 為劃分點,分成在 sss 集合內的編號信號塔和不在的信號塔。

    于是我們可以預處理這部分的貢獻,只需要知道 iii 和集合 sss 即可。

    規范化的,令 g(i,s)=∑j∈?s∧j≠ik?cnt(idj,idi)?cnt(idi,idj)+∑j∈sk?cnt(idi,idj)+cnt(idj,idi)g(i,s)=\sum_{j\not\in s\wedge j\ne i}k*cnt(id_j,id_i)-cnt(id_i,id_j)+\sum_{j\in s}k*cnt(id_i,id_j)+cnt(id_j,id_i)g(i,s)=j?sj?=i?k?cnt(idj?,idi?)?cnt(idi?,idj?)+js?k?cnt(idi?,idj?)+cnt(idj?,idi?)

    先預處理 g(i,0)g(i,0)g(i,0),然后每次枚舉 sss 二進制下的一個 111(隨便拎個 lowbitlowbitlowbit 位)即可 O(1)O(1)O(1) 轉移。

    一個數從 ∈?s\not\in s?s 到變成 ∈s\in ss,先減去原來的代價再加上 ∈s\in ss 里面時計算的代價。

    時間復雜度 O(2mm)O(2^mm)O(2mm)

    那么 fff 數組的轉移就比較簡潔了:枚舉 sss 二進制下的 111 位置,取較小值。
    f(s)=min?i∈s{f(s?{i})+∣s∣g(i,s?{i})}f(s)=\min_{i\in s}\Big\{f(s-\{i\})+|s|g(i,s-\{i\})\Big\} f(s)=ismin?{f(s?{i})+sg(i,s?{i})}
    到此時空復雜度均為 O(m2m)O(m2^m)O(m2m)

    發現空間起飛了。其實觀察數據分布就會發現,mmm 的變化只有 111,對空間的限制明顯狠于時間。

    也就是說,難得的需要來考慮卡一下空間了。

    有各種大力優化空間的妙做法,但是從應試角度,以及出題角度,私以為這種做法以及可以應付了:

    轉移方程中 g(i,s)g(i,s)g(i,s) ,若 i∈si\in sis 則是不合法的,顯然不會被用到。

    換言之,對于每個 iii,最多只有 2222^{22}222 種狀態。

    如果只存合法狀態,內存就會 /2/2/2,在時間上而言是常數的東西這里就是個大優化了。

    考慮怎么減半?

    • 只用讓每個 sss 的前 i?1i-1i?1 位不動,(s&(1<<i)-1);
    • 其余位置整體右移一位,相當于 /2/2/2,(s^(s&(1<<i)-1))>>1;
    • 最后把兩者加起來即可。

    code

    #include <bits/stdc++.h> using namespace std; #define maxn 23 int n, m, k; int f[1 << maxn], g[maxn][1 << maxn - 1], cnt[maxn][maxn]; int lowbit( int x ) { return x & -x; } int main() {scanf( "%d %d %d", &n, &m, &k );for( int i = 1, x, lst = -1;i <= n;i ++ ) {scanf( "%d", &x ); x --;if( ~ lst ) ++ cnt[lst][x];lst = x;}for( int i = 0;i < m;i ++ ) {for( int j = 0;j < m;j ++ )if( i ^ j ) g[i][0] += k * cnt[j][i] - cnt[i][j];for( int s = 1;s < (1 << m);s ++ )if( ! (s >> i & 1) ) {int t = s ^ lowbit( s );int j = __builtin_ffs( s ) - 1;g[i][(s & (1<<i)-1) + ((s ^ (s & (1<<i)-1)) >> 1)] = g[i][(t & (1<<i)-1) + ((t ^ (t & (1<<i)-1)) >> 1)] + (k * cnt[i][j] + cnt[j][i]) - (k * cnt[j][i] - cnt[i][j]);}}memset( f, 0x3f, sizeof( f ) ); f[0] = 0;for( int s = 1;s < (1 << m);s ++ ) {int x = __builtin_popcount( s );for( int i = 0;i < m;i ++ )if( s >> i & 1 ) {int t = s ^ (1 << i);f[s] = min( f[s], f[t] + x * g[i][(t & (1<<i)-1) + ((t ^ (t & (1<<i)-1)) >> 1)] );}}printf( "%d\n", f[(1 << m) - 1] );return 0; }

    總結

    以上是生活随笔為你收集整理的[省选联考 2020 A/B 卷] 信号传递(状压dp + 卡空间)的全部內容,希望文章能夠幫你解決所遇到的問題。

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