SDOI2018 榮譽稱號

2022-08-13 14:33:15 字數 2781 閱讀 7708

感覺這種分析性質的題目我搞不動啊

花了好久才弄得大致明白了

首先可以看出這個限制使得\(n\)個點構成了一棵滿二叉樹

並且樹上每條長度為\((k+1)\)的自上往下的鏈都是\(m\)的倍數

所以我們可以想到每個點的\(a\)都對\(m\)取mod

我們可以發現第\(t\)層的物品的\(a\)一定與第\(t+k+1\)層的物品同餘

所以可以僅計算出上面的\(k+1\)層每個節點的\(a\)值,那麼下面的所有點的\(a\)值就都是唯一確定的了

下面的節點用處不大,我們直接把這棵樹弄成只有前\(k+1\)層節點的一棵樹

這樣我們可以發現乙個性質,就是對於每條從根到葉子的鏈,鏈上\(a\)的和一定是\(m\)的倍數

這樣節點數就最多只有\(2^=2048\)個了

那麼\(m\)也不大,我們就可以設計乙個狀態了:\(f[i][j]\)表示從節點\(i\)出發,一路上的\(a\)值之和\(\mod m = j\)的最小花費

答案顯然就是\(f[1][0]\)了

那麼轉移也應該是不難的

我們就列舉\(j\)是多少,再列舉這個節點的\(a\)值是多少

那麼這個\(dp\)的轉移就是\(f[u][i] = min(f[u][j] , f[ls][(i-j+m)\mod m] + f[rs][(i-j+m)\mod m] + 這個節點的a值修改為j的代價)\)

那麼現在的問題就只有如何處理處這個節點的a值修改為j的代價這個東西了

這玩意兒似乎不是很好求

所以我們要把ta預處理出來

設\(g[u][i]\)表示點u修改為\(i\)的代價

假設我們預處理時到了節點\(v\)

由於我們只\(dp\)了前\((k+1)\)層

所以考慮前\((k+1)\)層的哪些點修改會對這個點造成影響?

因為第\(t\)層的物品的\(a\)一定與第\(t+k+1\)層的物品同餘

所以就讓這個節點往上若干次\((k+1)\)步,直到跳到深度小於等於\((k+1)\)的節點\(u\)

那麼我們就考慮當點\(u\)修改為\(i\)時點\(v\)對於點\(u\)的貢獻

我們需要分情況討論一下:

當\(i >= a[v] : g[u][i] += (i - a[v]) * b[v]\)

當\(i < a[v] : g[u][i] += (m + i - a[v]) * b[v]\)

直接暴力更新的複雜度為\(o(nm)\),這樣就可以獲得\(70\)分了

那麼把這玩意兒化開

就是\(g[u][i] += i \times \sum_b[v] - \sum_ + [i < a[v]]m \times \sum_\)

前面兩項就對前\((k+1)\)的點記錄一下\(sum_b , sum_\)就行了

後面一項每次\(o(1)\)打乙個標記最後對於前\((k+1)\)層的每個節點做一遍字尾和就好了

這樣我們就處理出來了\(g\)

那麼正經的\(dp\)式子就是\(f[u][i] = min(f[u][j] , f[ls][(i-j+m)\mod m] + f[rs][(i-j+m)\mod m] + g[u][j])\)

#include#include#include#include# define ll long long

# define ls(now) (now << 1)

# define rs(now) (now << 1 | 1)

const int m = 10000005 ;

const int n = 5050 ;

const int e = 205 ;

using namespace std ;

int n , m , k ;

int a[m] , b[m] , dep[m] ;

ll f[n][e] , g[n][e] ;

ll sum[n][e] , sumb[n] , sumab[n] ;

unsigned int sa, sb, sc;

unsigned int rng61()

inline void clear()

void read()

}inline int par(int u)

int main()

for(int i = 1 ; i < (1 << (k + 1)) ; i ++)

for(int j = m - 1 ; j >= 0 ; j --)

for(int u = (1 << k) ; u < (1 << (k + 1)) ; u ++)

for(int i = 0 ; i < m ; i ++)

f[u][i] = g[u][i] ;

for(int u = (1 << k) - 1 ; u >= 1 ; u --)

for(int i = 0 ; i < m ; i ++)

for(int j = 0 ; j < m ; j ++)

f[u][i] = min( f[u][i] , f[ls(u)][(i - j + m) % m] + f[rs(u)][(i - j + m) % m] + g[u][j] ) ;

printf("%lld\n",f[1][0]) ;

} return 0 ;

}

喜訊 知呱呱喜獲「匠心專業優秀機構」榮譽稱號

3 月 1 日,2019 年度北京技術市場協會會員大會在京成功召開。科技部 北京市科委 北京技術市場管理辦公室 京津冀技術轉移協同創新聯盟天津 河北秘書處領導出席會議。協會副理事長李建強致開幕詞,協會執行副理事長兼秘書長劉軍向大會做了題為 講誠信鑄品牌 攜手前行 的 2018 年度工作報告。www....

SDOI2018 戰略遊戲

給定一張 n 個點 m 條邊的無向聯通圖,共有 q 次操作,每次操作選擇一些點作為關鍵點,詢問有多少個點滿足刪去該點及與其相鄰的邊後,至少有兩個關鍵點不能互相到達。n,q leq 10 5,m leq 2 cdot 10 5,sum s leq 2 cdot 10 5 還是挺簡單的。就是圓方樹 虛樹...

SDOI2018 戰略遊戲

題目 圓方樹其實並沒有那麼難 圓方樹的構建比較簡單,就是乙個tarjan把點雙跑出來,對於每乙個點雙我們多建乙個方點,把原圖中的點稱為圓點,將點雙內所有圓點向方點連邊,之後我們就得到了原圖的圓方樹 關於圓方樹的性質,zyb大爺在他的題解裡寫了很多,這裡就不再抄一遍了 至於這道題,就是把圓點拿出來建棵...