2024.10.10
今日总结:
写完了南外的专题中的D:subsequence
首先这道题可以用简单的dp来做时间复杂度是O(n^2)这显然是一分没有的。
然后考虑优化可以用平衡树维护dp值的差分,每次在平衡树上二分找到使第二种策略最优解的位置
然后插入一个位置,对后缀进行区间加时间复杂度是O(nlogn)就能过。
但是仔细思考了一下正解:
正解是考虑分块,对于每一个内的凸壳,每次在凸壳上二分寻找最大值,选择元素将他删除,
然后重构凸壳这种时间复杂度是O(nsqrt(n)logn)的在时间上已经有了很大进步,
继续考虑优化,还可以在这个的基础上用用一个指针来代替二分然后就得到了一个O(nsqrt(n))的最优做法
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
#define l_son dp[v][0]
#define r_son dp[v][1]
int n,rt;
int a[N],siz[N],p[N],add[N];
int dp[N][2],fa[N];
inline void push_up(int v)
{
siz[v] = siz[l_son] + siz[r_son];
siz[v] ++;
}
inline void Add(int v,int f)
{
p[v] += f;
add[v] += f;
}
inline void push_down(int v)
{
if(add[v])
{
if(l_son) Add(l_son,add[v]);
if(r_son) Add(r_son,add[v]);
add[v] = 0;
}
}
inline void root(int v)
{
int f = fa[v],gr = fa[f];
int sn = v == dp[f][1],son = dp[v][!sn];
if(gr) dp[gr][f == dp[gr][1]] = v;
dp[f][sn] = son;
dp[v][!sn] = f;
fa[v] = gr,fa[f] = v;
if(son) fa[son] = f;
push_up(f),push_up(v);
}
inline void splay(int v)
{
static int st[N],top;
st[top = 1] = v;
for(int i = v;fa[i];i = fa[i])
st[++ top] = fa[i];
for(int i = top;i >= 1;i --)
push_down(st[i]);
while(fa[v])
{
int f = fa[v],gr = fa[f];
if(gr) root(f == dp[gr][1] ^ v == dp[f][1] ? v : f);
root(v);
}
rt = v;
}
int tot;
inline int Find(int k)
{
int v = rt;
while(1)
{
if(siz[l_son] + 1 == k) return v;
if(siz[l_son] >= k) v = l_son;
else k -= siz[l_son] + 1,v = r_son;
}
}
inline int Find_rk(int v)
{
splay(v);
return siz[l_son] + 1;
}
inline void Insert(int &v,int k,int id)
{
if(!v)
{
v = id;
push_up(v);
return;
}
if(siz[l_son] >= k - 1)
{
Insert(l_son,k,id);
fa[l_son] = v;
}
else
{
Insert(r_son,k - siz[l_son] - 1,id);
fa[r_son] = v;
}
push_up(v);
}
int pos;
inline void B_Find(int v,int a,int pre,int &pos)
{
if(!v) return;
push_down(v);
int now = pre + siz[l_son] + 1;
if(a * now >= p[v])
{
pos = v;
B_Find(l_son,a,pre,pos);
}
else B_Find(r_son,a,pre + siz[l_son] + 1,pos);
}
inline void Print(int v,int &ans)
{
if(!v) return;
push_down(v);
Print(l_son,ans);
ans += p[v];
printf("%lld ",ans);
Print(r_son,ans);
}
signed main()
{
scanf("%lld",&n);
for(int i = 1;i <= n;i ++)
scanf("%lld",&a[i]);
rt = 1;
push_up(rt);
p[rt] = a[1];
for(int i = 2;i <= n;i ++)
{
B_Find(rt,a[i],0,pos = i);
if(pos != i) splay(pos);
int rk = pos == i ? i : Find_rk(pos);
p[i] = a[i] * rk;
Insert(rt,rk,i);
splay(i);
if(dp[i][1]) Add(dp[i][1],a[i]);
}
int ans = 0;
Print(rt,ans);
return 0;
}
复习了拓扑排序。和有负环的图论,和tarjian算法