D. MEX Tree
这题需要维护一条链,如果在同一条链上,但是
不在这条链上,那么
设表示包含
的路径数,然后算出每个
后容斥就好了。
具体看写法。
这题2400难度,果然2400难度的题都是有一些想法但是写不出来的题。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
int n ;
cin >> n ;
vector<vector<int>> g(n + 1) ;
for(int i = 1 ; i <= n - 1 ; i ++)
{
int u , v ;
cin >> u >> v ;
g[u].push_back(v) ;
g[v].push_back(u) ;
}
vector<int> f(n + 1 , 0) ;
vector<int> siz(n + 1 , 0) ;
vector<int> siz2(n + 1 , 0) ;
function<void(int , int)> dfs = [&](int fa , int u)
{
siz[u] = 1 ;
f[u] = fa ;
for(auto v : g[u])
{
if(v == fa) continue ;
dfs(u , v) ;
siz[u] += siz[v] ;
}
} ;
dfs(0 , 0) ;
function<void(int , int , int)> dfs2 = [&](int fa , int u , int num)
{
siz2[u] = num ;
for(auto v : g[u])
{
if(v == fa) continue ;
dfs2(u , v , num) ;
}
} ;
for(auto v : g[0]) dfs2(0 , v , siz[v]) ;
//ans[i]��ʾ����[0 , i - 1]��·����
vector<long long> ans(n + 2 , 0) ;
ans[0] = 1ll * n * (n - 1) / 2 ;
ans[1] = ans[0] ;
auto cal = [&](int x)
{
return 1ll * x * (x - 1) / 2 ;
} ;
for(auto v : g[0]) ans[1] -= cal(siz[v]) ;
bool flag = true ;
int l = 0 , r = 0 ;
vector<bool> vis(n + 1 , false) ;
vis[0] = true ;
for(int i = 1 ; i <= n - 1 ; i ++)
{
int t = i ;
if(!vis[t])
{
while(!vis[t]) vis[t] = true , t = f[t] ;
if(!flag) ans[i + 1] = 0 ;
else
{
if(t == l) l = i ;
else if(t == r) r = i ;
else flag = false ;
}
}
if(!flag) ans[i + 1] = 0 ;
else
{
if(r == 0) ans[i + 1] = 1ll * siz[l] * (n - siz2[l]) ;
else ans[i + 1] = 1ll * siz[l] * siz[r] ;
}
}
for(int i = 0 ; i <= n - 1 ; i ++) ans[i] -= ans[i + 1] ;
for(int i = 0 ; i <= n ; i ++) cout << ans[i] << " \n"[i == n] ;
}
return 0 ;
}
E. Partition Game
决策单调性分治优化dp模板题。
决策点肯定是单调的,脑补下就知道了。
不过算贡献时候需要用双端队列剪枝,均摊下来复杂度是对的。
指针移动的复杂度我是分成L和R两部分证明 L的移动是最优决策点的移动 R的移动相当于是二叉树每个节点代表的区间长度和 都是n*logn
UPD:好吧,我承认这道题用线段树优化dp很傻逼。jiangly,yyds。
#include<bits/stdc++.h>
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n , k ;
cin >> n >> k ;
vector<int> a(n + 1) ;
for(int i = 1 ; i <= n ; i ++) cin >> a[i] ;
vector<vector<int>> dp(n + 1 , vector<int>(k + 1 , 2000000000)) ;
dp[0][0] = 0 ;
vector<int> nxt(n + 1 , n + 1) ;
vector<int> pos(n + 1 , n + 1) ;
for(int i = n ; i >= 1 ; i --)
{
if(pos[a[i]] > n)
{
pos[a[i]] = i ;
}
else
{
nxt[i] = pos[a[i]] ;
pos[a[i]] = i ;
}
}
vector<int> lst(n + 1 , 0) ;
vector<int> po(n + 1 , n + 1) ;
for(int i = 1 ; i <= n ; i ++)
{
if(po[a[i]] > n)
{
po[a[i]] = i ;
}
else
{
lst[i] = po[a[i]] ;
po[a[i]] = i ;
}
}
vector<int> q(n + 10 , 0) ;
int L = 1 , R = 0 ;
int res = 0 ;
auto cal = [&](int l , int r)
{
while(R < r)
{
R ++ ;
if(lst[R] >= L) res += R - lst[R] ;
}
while(L > l)
{
L -- ;
if(nxt[L] <= R) res += nxt[L] - L ;
}
while(R > r)
{
if(lst[R] >= L) res -= R - lst[R] ;
R -- ;
}
while(L < l)
{
if(nxt[L] <= R) res -= nxt[L] - L ;
L ++ ;
}
return res ;
} ;
function<void(int , int , int , int , int)> solve = [&](int t , int l1 , int r1 , int l2 , int r2)
{
if(l2 > r2) return ;
int mid = (l2 + r2) / 2 ;
int id = l1 ;
for(int i = l1 ; i <= min(r1 , mid) ; i ++)
{
if(i > mid) continue ;
int sum = dp[i - 1][t - 1] + cal(i , mid) ;
if(sum < dp[mid][t])
{
dp[mid][t] = sum ;
id = i ;
}
}
solve(t , l1 , id , l2 , mid - 1) ;
solve(t , id , r1 , mid + 1 , r2) ;
} ;
for(int j = 1 ; j <= k ; j ++) solve(j , 1 , n , 1 , n) ;
cout << dp[n][k] << '\n' ;
return 0 ;
}
这篇博客讨论了图论中的Dijkstra算法及其在链式结构上的应用,同时介绍了如何利用决策单调性优化动态规划模板,解决复杂问题。文章通过两个具体的题目实例——D.MEXTree和PartitionGame,详细阐述了解题思路和代码实现,涉及链的路径计数和双端队列剪枝等技术。
273

被折叠的 条评论
为什么被折叠?



