1455G Forbidden Value(資料結構優化dp+啟發式合併)

未已優發表於2020-12-03

1455G Forbidden Value(資料結構優化dp+啟發式合併)

Educational Codeforces Round 99 (Rated for Div. 2)

G. Forbidden Value

題面Forbidden Value

題意:給一個初始值為 0 0 0 的變數 x x x 以及一段只包含 s e t 、 i f 、 e n d set、if、end setifend 的簡易程式碼段,程式碼 “ s e t   y   v set~y~v set y v” 表示將 x x x 賦值為 y y y,或者花費 v v v 的代價不執行該語句,程式碼塊 i f   y   . . .   e n d if~y~...~end if y ... end 為條件語句塊,當 x x x 的值等於 y y y 時才會進入該程式碼塊,程式碼塊可以巢狀。在程式的執行過程當中一旦 x x x 的值變成了 s s s 就會立馬崩潰,現在問至少要花費多少的代價才能讓程式正常跑完。

範圍 1 ≤ n ≤ 2 e 5 , 1 ≤ s ≤ 2 e 5 , 0 ≤ y ≤ 2 e 5 , 1 ≤ v ≤ 1 e 9 1 \le n \le 2e5, 1 \le s \le 2e5, 0 \le y \le 2e5, 1 \le v \le 1e9 1n2e5,1s2e5,0y2e5,1v1e9

分析: 可以先考慮基本的 d p dp dp d p i j dp_{ij} dpij 表示執行完第 i i i 條語句後 x x x 的值為 j j j 所需要的最小代價。若程式碼段中只存在 s e t set set 語句的話這個 d p dp dp 很好寫,只需要注意進行離散化。現在考慮 i f   . . .   e n d if~...~end if ... end 語句塊,可以發現處理語句塊內部只是與本道題本質相同規律減少的子問題,因此我們可以對該問題進行遞迴的求解,假設進入內部 i f   . . .   e n d if~...~end if ... end 語句塊所需要的代價為 c o s t cost cost,那麼在處理完內部 i n n e r _ d p inner\_dp inner_dp 後與外部 o u t e r _ d p outer\_dp outer_dp 進行合併時 o u t e r _ d p i j = m i n ( o u t e r _ d p i j , c o s t + i n n e r _ d p i j ) outer\_dp_{ij} = min(outer\_dp_{ij}, cost + inner\_dp_{ij}) outer_dpij=min(outer_dpij,cost+inner_dpij) 即可,總體的時間複雜度為 O ( n 2 ) O(n^2) O(n2),考慮進行優化。

兩個大方向可以進行優化:

① 對於一條 s e t   y   v set~y~v set y v 語句,除了 j = = y j == y j==y 其餘所有的 j j j 為了保持原來的值都需要增加 v v v 的代價,而 j = = y j == y j==y 在這條語句過後一定會保持 j = = y j == y j==y,因此在這條語句之前 j j j 可以是任何值,所以我們應當取其中代價最小的。注意到每次只有一個 j j j 的變化不同,其他 j j j 的變化都相同,因此我們可以設定一個偏移量 o f f s e t offset offset 儲存所有 j j j 的變化量,只修改一個 j = = y j == y j==y 的值,這裡我們需要一個快速單點修改以及求全域性最小值的功能,STL 中的容器 S e t Set Set 可以滿足,時間複雜度從 O ( n ) O(n) O(n) 降低為 O ( l o g n ) O(logn) O(logn)

② 上述的樸素 d p dp dp 合併方式的時間複雜度為 O ( n 2 ) O(n^2) O(n2),這裡可以使用啟發式合併的方式,每次合併的時候都是 s i z e size size 較小的 d p dp dp 合併至 s i z e size size 較大的 d p dp dp,時間複雜度從 O ( n 2 ) O(n^2) O(n2) 降低為 O ( n l o g n ) O(nlogn) O(nlogn),不過這裡我們需要維護 S e t Set Set 等資料結構,時間複雜度為 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn)

通過上述的優化,資料結構 s e t set set m a p map map 優化的 d p dp dp 配合啟發式合併,將時間複雜度減小到 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n),足以通過本題。

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
 
inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
 
const int MAXN = 2e5 + 10;
const int INF = 1e18;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
 
int n, s;
 
struct Node
{
	int offset;
	map<int, int> mp;
	multiset<int> ms;
};
 
vector<Node> vec;
 
void update(Node &node, int y, int v)
{
	if (node.mp.count(y))
	{
		node.ms.erase(node.ms.find(node.mp[y]));
	}
	node.mp[y] = v;
	node.ms.insert(v);
}
 
signed main()
{
    n = read(), s = read();
    int skip = 0;
    vec.push_back({});
    vec.back().offset = 0;
    vec.back().mp[0] = 0;
    vec.back().ms.insert(0);
    for (int i = 0; i < n; i++)
    {
    	string op;
    	int y, v;
    	cin >> op;
    	if (op[0] == 's')
    	{
    		y = read(), v = read();
    		if (skip) continue;
    		int minV = *vec.back().ms.begin() - v;
    		vec.back().offset += v;
    		if (y != s)
    		{
    			update(vec.back(), y, minV);
    		}
    	}
    	else if (op[0] == 'i')
    	{
    		y = read();
    		if (!skip && vec.back().mp.count(y))
    		{
    			int val = vec.back().mp[y] + vec.back().offset;
    			vec.back().ms.erase(vec.back().ms.find(vec.back().mp[y]));
    			vec.back().mp.erase(y);
    			vec.push_back({});
    			vec.back().offset = 0;
    			update(vec.back(), y, val);
    		}
    		else
    		{
    			skip++;
    		}
    	}
    	else
    	{
    		if (skip) skip--;
    		else
    		{
    			if (vec[vec.size() - 1].mp.size() > vec[vec.size() - 2].mp.size())
    			{
    				swap(vec[vec.size() - 1], vec[vec.size() - 2]);
    			}
    			auto &outer = vec[vec.size() - 2];
    			for (auto x : vec.back().mp)
    			{
    				if (!outer.mp.count(x.first) || (outer.offset + outer.mp[x.first] > vec.back().offset + x.second))
    				{
    					int val = vec.back().offset + x.second - outer.offset;
    					update(outer, x.first, val);
    				}
    			}
    			vec.pop_back();
    		}
    	}
    }
    cout << vec.back().offset + *vec.back().ms.begin() << endl;
    return 0;
}

【END】感謝觀看

相關文章