Codeforces 1455G - Forbidden Value(map 啟發式合併+DP)
阿新 • • 發佈:2021-10-19
map 啟發式合併優化樹形 dp
,並且程式中變數的值時刻不等於 \(s\)。對於一個子樹而言,初始 \(dp_{x,v}=0\),其餘 \(dp_{x,i}=\infty\),其中 \(v\) 為進入這棵子樹時變數的值,那麼我們按順序遍歷這個節點所有子節點 \(y\),分情況討論:
中最小的元素更新 \(dp_{x,j}\)。對於一個非賦值操作的兒子 \(y\),我們考慮啟發式合併如果該節點 \(dp\) 陣列的大小 \(>y\) 節點 \(dp\) 陣列的大小就暴力用 \(dp_y\) 中的值去更新 \(dp_x\),否則交換 \(dp_x,dp_y\),對 \(dp_x\) 打上一個 \(+dp_{x,v}\) 的標記,然後暴力將現 \(dp_y\) 也就是原 \(dp_x\) 中的元素插入 \(dp_x\) 即可。
首先這個 if
與 end
配對的結構顯然形成一個樹形結構,考慮把這棵樹建出來,於是這個程式的結構就變為,對樹進行一遍 DFS,到達某個節點時,按照順序遍歷其所有兒子,如果兒子是葉子節點則執行賦值操作,否則進入到對應子樹中執行相應操作,如果一個節點所有兒子對應的操作都被執行了就回溯,不難發現該過程與原程式等價。
考慮樹形 dp。可以發現對於一棵非葉節點,在首次進入這棵子樹時,程式中 \(x\) 的值已經確定了——就是 if v
中 \(v\) 的值。因此設 \(dp_{x,j}\) 表示最少花費多少的代價,才能使執行完 \(x\) 子樹內的操作後,程式中變數的值為 \(j\)
- 該子節點為賦值操作:那麼不妨假設該子節點將變數的值改為 \(p\),刪除該操作需要 \(c\) 的代價,那麼:
- \(dp_{x,i}\leftarrow dp_{x,i}+c(i\ne p)\)
- \(dp_{x,i}\leftarrow\min\{dp_{x,j}\}(i=p)\)
- 該子節點不是賦值操作:假設這個子樹對應的語句為
if v
,那麼有:- \(dp_{x,i}\leftarrow \min(dp_{x,i},dp_{x,v}+dp_{y,i})(i\ne v)\)
- \(dp_{x,i}\leftarrow dp_{x,v}+dp_{y,i}(i=v)\)
- \(dp_{x,i}\leftarrow \min(dp_{x,i},dp_{x,v}+dp_{y,i})(i\ne v)\)
每更新完一步之後都要把 \(dp_{x,s}\) 改為 \(\infty\),因為要保證變數的值時刻不等於 \(s\)。
直接做是平方的。可以發現對於某個 \(x\) 而言,有效的 \(dp_{x,i}\) 的個數是 \(\mathcal O(\text{size}_x)\) 級別的,這不禁讓我們想到啟發式合併。我們考慮用個 map
維護所有 DP 值。對於一個是賦值操作的兒子 \(y\),我們考慮對其打一個整體加 \(+c\) 的 tag,然後用個 multiset
維護其所有 DP 值,取出 multiset
時間複雜度 \(n\log^2n\)。細節略有一點點多。
const int MAXN=2e5;
const ll INF=0x3f3f3f3f3f3f3f3fll;
int n,m=1,k,ban,cur=1,val[MAXN+5],fa[MAXN+5];
vector<pii> to[MAXN+5];
int cst[MAXN+5],asv[MAXN+5];
ll tag[MAXN+5];
map<int,ll> dp[MAXN+5];
multiset<ll> st[MAXN+5];
ll get(int x,int v){return (dp[x].count(v))?dp[x][v]:INF;}
void relax(int x){
if(dp[x].count(ban)){
st[x].erase(st[x].find(dp[x][ban]));
dp[x][ban]=INF;st[x].insert(INF);
}
}
void dfs(int x){
// printf("dfs %d\n",x);
dp[x][val[x]]=0;st[x].insert(0);relax(x);
for(pii p:to[x]){
int op=p.fi,id=p.se;
if(op==1){
ll v=min((*st[x].begin())+tag[x],INF);tag[x]+=cst[id];
if(dp[x].count(asv[id])) st[x].erase(st[x].find(dp[x][asv[id]]));
dp[x][asv[id]]=v-tag[x];st[x].insert(v-tag[x]);
} else {
dfs(id);ll nd=get(x,val[id])+tag[x];
if(dp[x].size()>dp[id].size()){
for(pair<int,ll> pp:dp[id]){
int pos=pp.fi;ll v=pp.se+tag[id]+nd,ori=get(x,pos)+tag[x];chkmin(v,INF);
// printf("%d %d %lld\n",id,pp.fi,v);
if(dp[x].count(pos)) st[x].erase(st[x].find(dp[x][pos]));
if(pos==val[id]) dp[x][pos]=v-tag[x];
else dp[x][pos]=min(v,ori)-tag[x];
st[x].insert(dp[x][pos]);
}
} else if(nd<INF) {
// printf("%lld\n",nd);
swap(dp[x],dp[id]);swap(st[x],st[id]);
swap(tag[x],tag[id]);tag[x]+=nd;
for(pair<int,ll> pp:dp[id]){
int pos=pp.fi;ll v=min(pp.se+tag[id],INF);
if(pos!=val[id]){
chkmin(v,get(x,pos)+tag[x]);
if(dp[x].count(pos)) st[x].erase(st[x].find(get(x,pos)));
dp[x][pos]=v-tag[x];st[x].insert(dp[x][pos]);
}
}
}
} relax(x);
// for(pair<int,ll> pp:dp[x]) printf("dp[%d][%d]=%lld\n",x,pp.fi,pp.se+tag[x]);
}
}
int main(){
scanf("%d%d",&n,&ban);val[1]=0;
for(int i=1;i<=n;i++){
static char buf[10];scanf("%s",buf+1);
if(buf[1]=='s'){
int x,v;scanf("%d%d",&x,&v);
cst[++k]=v;asv[k]=x;to[cur].pb(mp(1,k));
} else if(buf[1]=='i'){
int x;scanf("%d",&x);val[++m]=x;
to[cur].pb(mp(2,m));fa[m]=cur;cur=m;
} else cur=fa[cur];
}
// for(int i=1;i<=m;i++) for(pii p:to[i])
// printf("%d -> %d %d\n",i,p.fi,p.se);
dfs(1);printf("%lld\n",(*st[1].begin())+tag[1]);
return 0;
}