花了好久才弄得大致明白了
首先可以看出這個限制使得\(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大爺在他的題解裡寫了很多,這裡就不再抄一遍了 至於這道題,就是把圓點拿出來建棵...